[update] SSD1306サポート,RP2040を介したPWMなどを追加

This commit is contained in:
ocogeclub 2023-08-18 11:16:52 +09:00
parent a97a207d55
commit 82db8193d2
37 changed files with 3123 additions and 842 deletions

View File

@ -14,7 +14,7 @@ const ugj_const = {
localStorage_fname: 'ocoge.json', localStorage_fname: 'ocoge.json',
error_ja_all: 'エラーが発生しました。\n『おこげ倶楽部』までお問い合わせください。', error_ja_all: 'エラーが発生しました。\n『おこげ倶楽部』までお問い合わせください。',
pig: 'pigpio', pig: 'pigpio',
rg: 'rgpio', rg: '@ocoge/rgpio',
i2c_defbus: '1', // 文字列リテラルで指定 i2c_defbus: '1', // 文字列リテラルで指定
lang: 'js', lang: 'js',
dev_hash: '4e9205f9b7e571bec1aa52ab7871f420684fcf96149672a4d550a95863d6b072' dev_hash: '4e9205f9b7e571bec1aa52ab7871f420684fcf96149672a4d550a95863d6b072'
@ -309,6 +309,11 @@ class appTool {
return formatted; return formatted;
} }
// メインプロセスNode-Canvas 呼び出しOLED テキスト画像生成
async textToRGBA(text, font, color, start_x, start_y) {
return await this.ipcRenderer.invoke('text_to_rgba', text, font, color, start_x, start_y);
}
} }
// ブラウザ動作用 // ブラウザ動作用

View File

@ -209,7 +209,8 @@ Blockly.defineBlocksWithJsonArray([{
"helpUrl": "", "helpUrl": "",
"style": "gpio_blocks" "style": "gpio_blocks"
}]); }]);
Blockly.JavaScript['ugj_gpio_open'] = function (block) { javascript.javascriptGenerator.forBlock['ugj_gpio_open'] = function (block, generator) {
// Blockly.JavaScript['ugj_gpio_open'] = function (block) {
Blockly.JavaScript.provideFunction_( Blockly.JavaScript.provideFunction_(
'require_gpio', [`const _rg = require('${apptool.gpio_lib}');`] 'require_gpio', [`const _rg = require('${apptool.gpio_lib}');`]
); );
@ -666,9 +667,9 @@ Blockly.Blocks['oc_i2c_open'] = {
Blockly.JavaScript['oc_i2c_open'] = function (block) { Blockly.JavaScript['oc_i2c_open'] = function (block) {
var value_addr = Blockly.JavaScript.valueToCode(block, 'addr', Blockly.JavaScript.ORDER_ATOMIC); var value_addr = Blockly.JavaScript.valueToCode(block, 'addr', Blockly.JavaScript.ORDER_ATOMIC);
var variable_i2c_hand = Blockly.JavaScript.nameDB_.getName(block.getFieldValue('i2c_hand'), Blockly.Names.NameType.VARIABLE); var variable_i2c_hand = Blockly.JavaScript.nameDB_.getName(block.getFieldValue('i2c_hand'), Blockly.Names.NameType.VARIABLE);
Blockly.JavaScript.provideFunction_( // Blockly.JavaScript.provideFunction_(
'require_gpio', require_gpio // 'require_gpio', require_gpio
); // );
var code = `${variable_i2c_hand} = await _rg.i2c_open(${apptool.i2c_bus}, ${value_addr});\n`; var code = `${variable_i2c_hand} = await _rg.i2c_open(${apptool.i2c_bus}, ${value_addr});\n`;
return code; return code;
}; };

View File

@ -14,7 +14,7 @@ apptool.blocks_dir = (function () {
var flyout_contents = []; var flyout_contents = [];
const registerCategory = (category_dir, subcategories) => { const registerCategory = (category_dir, subcategories) => {
for (let sbctgr of subcategories) { for (let sbctgr of subcategories) {
let fname = apptool.blocks_dir + category_dir + '/' + sbctgr + "/index.js"; let fname = apptool.blocks_dir + category_dir + '/' + sbctgr;
let script = document.createElement('script'); let script = document.createElement('script');
script.type = 'text/javascript'; script.type = 'text/javascript';
script.src = fname; script.src = fname;
@ -25,11 +25,12 @@ const registerCategory = (category_dir, subcategories) => {
} }
// センサーカテゴリ // センサーカテゴリ
registerCategory('sensors', [ // カテゴリディレクトリ名 registerCategory('sensors', [ // サブカテゴリファイル名
"amg8833", "amg8833.js",
"paj7620", "paj7620.js",
"bme280", "bme280.js",
"ssd1306.js",
// "dht11", // "dht11",
// "pico_slave", // "pico_slave",
"z-line" // フライアウト下端の不可視ライン。スクリプトにカテゴリ名を含むので注意 "z-line.js" // フライアウト下端の不可視ライン。スクリプトにカテゴリ名を含むので注意
]); ]);

View File

@ -56,11 +56,13 @@ Blockly.JavaScript['ugj_grideye_init'] = function (block) {
// Blockly.JavaScript.provideFunction_( // Blockly.JavaScript.provideFunction_(
// 'require_gpio', [`const _pi = require('` + apptool.gpio_lib + `');`] // 'require_gpio', [`const _pi = require('` + apptool.gpio_lib + `');`]
// ); // );
let modpath = apptool.path.join(apptool.blocks_dir, 'sensors', 'amg8833', `AMG8833x.js`); // let modpath = apptool.path.join(apptool.blocks_dir, 'sensors', 'amg8833', `AMG8833x.js`);
Blockly.JavaScript.provideFunction_( Blockly.JavaScript.provideFunction_(
'require_amg8833', [`const _amg8833 = require('${modpath}');`] 'require_amg8833', [`const _amg8833 = require('@ocoge/amg8833');`]
// 'require_amg8833', [`const _amg8833 = require('${modpath}');`]
); );
var code = `await _amg8833.init(${apptool.i2c_bus}, ${dropdown_addr}, window.addEventListener);\n`; var code = `await _amg8833.init(_rg, ${apptool.i2c_bus}, ${dropdown_addr});\n`;
// var code = `await _amg8833.init(${apptool.i2c_bus}, ${dropdown_addr}, window.addEventListener);\n`;
return code;// return code;//
}; };
/********************** */ /********************** */

View File

@ -1,55 +0,0 @@
'use strict';
const err_msg = 'AMG8833 is already opened. Please close old connection to use new one.';
const pig = require(`${apptool.gpio_lib}`);
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
*/

View File

@ -21,15 +21,16 @@ Blockly.JavaScript['ugj_bme280'] = function (block) {
// 'require_gpio', [`const _pi = require('` + apptool.gpio_lib + `');`] // 'require_gpio', [`const _pi = require('` + apptool.gpio_lib + `');`]
// ); // );
// let modpath = apptool.path.join(apptool.blocks_dir, 'sensors', 'bme280', 'BME280x.js'); // let modpath = apptool.path.join(apptool.blocks_dir, 'sensors', 'bme280', 'BME280x.js');
let modpath = apptool.path.join(apptool.blocks_dir, 'sensors', 'bme280', `BME280x.js`); // let modpath = apptool.path.join(apptool.blocks_dir, 'sensors', 'bme280', `BME280x.js`);
Blockly.JavaScript.provideFunction_( Blockly.JavaScript.provideFunction_(
'require_bme280', [`const _bme280 = require('${modpath}');`] 'require_bme280', [`const _bme280 = require('@ocoge/bme280');`]
// 'require_bme280', [`const _bme280 = require('${modpath}');`]
); );
var code = `const options = { var code = `const options = {
i2cBusNo: ${apptool.i2c_bus}, i2cBusNo: ${apptool.i2c_bus},
i2cAddress: ${dropdown_addr} i2cAddress: ${dropdown_addr}
}; };
await _bme280.init(options); await _bme280.init(_rg, options);
let _thp = await _bme280.readSensorData(); let _thp = await _bme280.readSensorData();
let _bmedata = []; let _bmedata = [];
_bmedata[0] = Math.round(_thp.temperature_C * 10) / 10; _bmedata[0] = Math.round(_thp.temperature_C * 10) / 10;

View File

@ -35,11 +35,12 @@ Blockly.JavaScript['ugj_gesture_init'] = function (block) {
// Blockly.JavaScript.provideFunction_( // Blockly.JavaScript.provideFunction_(
// 'require_gpio', [`const _pi = require('` + apptool.gpio_lib + `');`] // 'require_gpio', [`const _pi = require('` + apptool.gpio_lib + `');`]
// ); // );
let modpath = apptool.path.join(apptool.blocks_dir, 'sensors', 'paj7620', 'PAJ7620x.js'); // let modpath = apptool.path.join(apptool.blocks_dir, 'sensors', 'paj7620', 'PAJ7620x.js');
Blockly.JavaScript.provideFunction_( Blockly.JavaScript.provideFunction_(
'require_paj7620', [`const _paj7620 = require('${modpath}');`] 'require_paj7620', [`const _paj7620 = require('@ocoge/paj7620');`]
// 'require_paj7620', [`const _paj7620 = require('${modpath}');`]
); );
var code = `await _paj7620.init(${apptool.i2c_bus}, ${value_i2c_addr}, window.addEventListener); var code = `await _paj7620.init(_rg, ${apptool.i2c_bus}, ${value_i2c_addr});
`; `;
return code; return code;
}; };

636
blocks/sensors/ssd1306.js Normal file
View File

@ -0,0 +1,636 @@
/*************** */
/** SSD1306 OLED */
/*************** */
Blockly.Blocks['oc_oled_init'] = {
init: function () {
this.appendDummyInput()
.appendField("有機ELディスプレイ")
.appendField(new Blockly.FieldVariable("oled"), "i2c_device")
.appendField("に接続");
this.appendDummyInput()
.setAlign(Blockly.ALIGN_RIGHT)
.appendField("I2Cアドレス")
.appendField(new Blockly.FieldDropdown([["0x3c", "0x3c"], ["0x3d", "0x3d"]]), "i2c_addr");
this.appendDummyInput()
.setAlign(Blockly.ALIGN_RIGHT)
.appendField("画面サイズ(幅x高さ)")
.appendField(new Blockly.FieldDropdown([["128x64", "128x64"], ["128x32", "128x32"], ["96x16", "96x16"]]), "disp_size");
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setStyle('sensor_blocks');
this.setTooltip("I2C 接続の SSD1306 有機ELディスプレイを使えるようにします。");
this.setHelpUrl("");
}
};
javascript.javascriptGenerator.forBlock['oc_oled_init'] = function (block, generator) {
var variable_i2c_device = generator.nameDB_.getName(block.getFieldValue('i2c_device'), Blockly.Names.NameType.VARIABLE);
var dropdown_i2c_addr = block.getFieldValue('i2c_addr');
var dropdown_disp_size = block.getFieldValue('disp_size');
Blockly.JavaScript.provideFunction_(
'require_oled', [`const _oled = require('@ocoge/ssd1306');`]
);
let size_x, size_y;
if (dropdown_disp_size == '128x64') {
size_x = 128;
size_y = 64;
}
var code = `var _opts = {
width: ${size_x},
height: ${size_y},
address: ${dropdown_i2c_addr},
bus: ${apptool.i2c_bus}
};
${variable_i2c_device} = new _oled(_rg, _opts);
${variable_i2c_device}.clearDisplay();
${variable_i2c_device}.turnOnDisplay();
`;
return code;
};
/** Draw Line */
Blockly.Blocks['oc_oled_drawline'] = {
init: function () {
this.appendValueInput("start_x")
.setCheck("Number")
.appendField(new Blockly.FieldVariable("oled"), "oled_hand")
.appendField("に線を描く:始点 (");
this.appendValueInput("start_y")
.setCheck("Number")
.appendField(",");
this.appendValueInput("end_x")
.setCheck("Number")
.appendField(") 終点 (");
this.appendValueInput("end_y")
.setCheck("Number")
.appendField(",");
this.appendDummyInput()
.appendField(") 色")
.appendField(new Blockly.FieldDropdown([["白", "1"], ["黒", "0"]]), "color");
this.setInputsInline(true);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setStyle('sensor_blocks');
this.setTooltip("OLED に線を描きます。");
this.setHelpUrl("");
}
};
javascript.javascriptGenerator.forBlock['oc_oled_drawline'] = function (block, generator) {
var variable_oled_hand = generator.nameDB_.getName(block.getFieldValue('oled_hand'), Blockly.Names.NameType.VARIABLE);
var value_start_x = generator.valueToCode(block, 'start_x', javascript.Order.ATOMIC);
var value_start_y = generator.valueToCode(block, 'start_y', javascript.Order.ATOMIC);
var value_end_x = generator.valueToCode(block, 'end_x', javascript.Order.ATOMIC);
var value_end_y = generator.valueToCode(block, 'end_y', javascript.Order.ATOMIC);
var dropdown_color = block.getFieldValue('color');
var code = `${variable_oled_hand}.drawLine(${value_start_x}, ${value_start_y}, ${value_end_x}, ${value_end_y}, ${dropdown_color});`;
return code;
};
/** 矩形塗りつぶし */
Blockly.defineBlocksWithJsonArray([{
"type": "oc_oled_fillrect",
"message0": "%1 に四角形を描画して塗りつぶす:左上座標 ( %2 , %3 ) 幅 %4 高さ %5 色 %6",
"args0": [
{
"type": "field_variable",
"name": "oled_hand",
"variable": "oled"
},
{
"type": "input_value",
"name": "left",
"check": "Number"
},
{
"type": "input_value",
"name": "top",
"check": "Number"
},
{
"type": "input_value",
"name": "width",
"check": "Number"
},
{
"type": "input_value",
"name": "height",
"check": "Number"
},
{
"type": "field_dropdown",
"name": "color",
"options": [
[
"白",
"1"
],
[
"黒",
"0"
]
]
}
],
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"tooltip": "OLED に四角形を描画して塗りつぶします。",
"helpUrl": "",
"style": "sensor_blocks"
}]);
javascript.javascriptGenerator.forBlock['oc_oled_fillrect'] = function (block, generator) {
var variable_oled_hand = generator.nameDB_.getName(block.getFieldValue('oled_hand'), Blockly.Names.NameType.VARIABLE);
var value_left = generator.valueToCode(block, 'left', javascript.Order.ATOMIC);
var value_top = generator.valueToCode(block, 'top', javascript.Order.ATOMIC);
var value_width = generator.valueToCode(block, 'width', javascript.Order.ATOMIC);
var value_height = generator.valueToCode(block, 'height', javascript.Order.ATOMIC);
var dropdown_color = block.getFieldValue('color');
var code = `${variable_oled_hand}.fillRect(${value_left}, ${value_top}, ${value_width}, ${value_height}, ${dropdown_color});\n`;
return code;
};
/** 文字を描画node-canvas */
Blockly.defineBlocksWithJsonArray([{
"type": "oc_oled_canvastext",
"message0": "%1 にテキストを表示 %2 フォント %3 %4 色 %5 %6 始点 x座標 %7 y座標 %8",
"args0": [
{
"type": "field_variable",
"name": "oled_hand",
"variable": "oled"
},
{
"type": "input_value",
"name": "text",
"check": "String"
},
{
"type": "field_dropdown",
"name": "font",
"options": [
[
"美咲ゴシック(8x8)",
"8px MisakiGothic"
],
[
"PixelMplus(10x10)",
"10px PixelMplus10"
],
[
"PixelMplus(12x12)",
"12px PixelMplus12"
]
]
},
{
"type": "input_dummy",
"align": "RIGHT"
},
{
"type": "field_dropdown",
"name": "color",
"options": [
[
"白",
"white"
],
[
"黒",
"black"
]
]
},
{
"type": "input_dummy",
"align": "RIGHT"
},
{
"type": "input_value",
"name": "start_x",
"check": "Number",
"align": "RIGHT"
},
{
"type": "input_value",
"name": "start_y",
"check": "Number",
"align": "RIGHT"
}
],
"inputsInline": false,
"previousStatement": null,
"nextStatement": null,
"style": "sensor_blocks",
"tooltip": "OLEDに指定のフォントで文字を描画しますnode-canvas利用。始点は1文字目の左下の座標です。",
"helpUrl": ""
}]);
javascript.javascriptGenerator.forBlock['oc_oled_canvastext'] = function (block, generator) {
var variable_oled_hand = generator.nameDB_.getName(block.getFieldValue('oled_hand'), Blockly.Names.NameType.VARIABLE);
var value_text = generator.valueToCode(block, 'text', javascript.Order.ATOMIC);
var value_start_x = generator.valueToCode(block, 'start_x', javascript.Order.ATOMIC);
var value_start_y = generator.valueToCode(block, 'start_y', javascript.Order.ATOMIC);
var dropdown_font = block.getFieldValue('font');
var dropdown_color = block.getFieldValue('color');
var code = `${variable_oled_hand}.drawRGBAImage(await apptool.textToRGBA(${value_text}, '${dropdown_font}', '${dropdown_color}', ${value_start_x}, ${value_start_y}), 0, 0);`;
return code;
};
Blockly.defineBlocksWithJsonArray([{
"type": "oc_oled_writestring",
"message0": "%1 に英数字を表示 %2 フォント %3 %4 色 %5 %6 始点 x座標 %7 y座標 %8",
"args0": [
{
"type": "field_variable",
"name": "oled_hand",
"variable": "oled"
},
{
"type": "input_value",
"name": "text",
"check": "String"
},
{
"type": "field_dropdown",
"name": "font",
"options": [
["oled_3x5", "oled_3x5"],
["tiny_4x6", "tiny_4x6"],
["oled_5x7", "oled_5x7"],
["small_6x8", "small_6x8"],
["sinclair_8x8", "sinclair_8x8"],
["sinclair_inverted_8x8", "sinclair_inverted_8x8"],
["tiny_8x8", "tiny_8x8"],
["cp437_8x8", "cp437_8x8"],
["myke2_8x9", "myke2_8x9"],
["small_8x12", "small_8x12"],
["tron_8x12", "tron_8x12"],
["retro_8x16", "retro_8x16"],
["medium_numbers_12x16", "medium_numbers_12x16"],
["big_numbers_14x24", "big_numbers_14x24"],
["arial_bold_16x16", "arial_bold_16x16"],
["arial_italic_16x16", "arial_italic_16x16"],
["arial_normal_16x16", "arial_normal_16x16"],
["big_16x16", "big_16x16"],
["franklin_gothic_normal_16x16", "franklin_gothic_normal_16x16"],
["hallfetica_normal_16x16", "hallfetica_normal_16x16"],
["nadianne_16x16", "nadianne_16x16"],
["sinclair_medium_16x16", "sinclair_medium_16x16"],
["sinclair_medium_inverted_16x16", "sinclair_medium_inverted_16x16"],
["swiss_721_outline_16x16", "swiss_721_outline_16x16"],
["various_symbols_16x16", "various_symbols_16x16"],
["dot_matrix_medium_16x22", "dot_matrix_medium_16x22"],
["dot_matrix_medium_zero_slash_16x22", "dot_matrix_medium_zero_slash_16x22"],
["dot_matrix_medium_numbers_only_16x22", "dot_matrix_medium_numbers_only_16x22"],
["arial_round_16x24", "arial_round_16x24"],
["ocr_a_extended_medium_16x24", "ocr_a_extended_medium_16x24"],
["sixteen_segment_16x24", "sixteen_segment_16x24"],
["grotesk_16x32", "grotesk_16x32"],
["grotesk_bold_16x32", "grotesk_bold_16x32"],
["retro_16x32", "retro_16x32"],
["various_symbols_16x32", "various_symbols_16x32"],
["various_symbols_v2_16x32", "various_symbols_v2_16x32"],
["dot_matrix_large_numbers_only_24x29", "dot_matrix_large_numbers_only_24x29"],
["inconsola_24x32", "inconsola_24x32"],
["ubuntu_24x32", "ubuntu_24x32"],
["ubuntu_bold_24x32", "ubuntu_bold_24x32"],
["dingbats1_extra_large_32x24", "dingbats1_extra_large_32x24"],
["various_symbols_32x32", "various_symbols_32x32"]
]
},
{
"type": "input_dummy",
"align": "RIGHT"
},
{
"type": "field_dropdown",
"name": "color",
"options": [
[
"白",
"1"
],
[
"黒",
"0"
]
]
},
{
"type": "input_dummy",
"align": "RIGHT"
},
{
"type": "input_value",
"name": "start_x",
"check": "Number",
"align": "RIGHT"
},
{
"type": "input_value",
"name": "start_y",
"check": "Number",
"align": "RIGHT"
}
],
"inputsInline": false,
"previousStatement": null,
"nextStatement": null,
"tooltip": "OLEDに指定の英字(数字)フォントで文字を描画します。始点は1文字目の左上の座標です。",
"helpUrl": "",
"style": "sensor_blocks"
}]);
javascript.javascriptGenerator.forBlock['oc_oled_writestring'] = function (block, generator) {
var variable_oled_hand = generator.nameDB_.getName(block.getFieldValue('oled_hand'), Blockly.Names.NameType.VARIABLE);
var value_text = generator.valueToCode(block, 'text', javascript.Order.ATOMIC);
var dropdown_font = block.getFieldValue('font');
var dropdown_color = block.getFieldValue('color');
var value_start_x = generator.valueToCode(block, 'start_x', javascript.Order.ATOMIC);
var value_start_y = generator.valueToCode(block, 'start_y', javascript.Order.ATOMIC);
Blockly.JavaScript.provideFunction_(
'require_fontpack', [`const _fontpack = require('oled-font-pack');`]
);
var code = `${variable_oled_hand}.setCursor(${value_start_x}, ${value_start_y});
${variable_oled_hand}.writeString(_fontpack.${dropdown_font}, 1, ${value_text}, ${dropdown_color}, false);
`;
return code;
};
/** 点を描画 */
Blockly.defineBlocksWithJsonArray([{
"type": "oc_oled_drawpixel",
"message0": "%1 に点を描画:座標 ( %2 , %3 ) 色 %4",
"args0": [
{
"type": "field_variable",
"name": "oled_hand",
"variable": "oled"
},
{
"type": "input_value",
"name": "x",
"check": "Number"
},
{
"type": "input_value",
"name": "y",
"check": "Number"
},
{
"type": "field_dropdown",
"name": "color",
"options": [
[
"白",
"1"
],
[
"黒",
"0"
]
]
}
],
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"tooltip": "OLED に点を描きます。",
"helpUrl": "",
"style": "sensor_blocks"
}]);
javascript.javascriptGenerator.forBlock['oc_oled_drawpixel'] = function (block, generator) {
var variable_oled_hand = generator.nameDB_.getName(block.getFieldValue('oled_hand'), Blockly.Names.NameType.VARIABLE);
var value_x = generator.valueToCode(block, 'x', javascript.Order.ATOMIC);
var value_y = generator.valueToCode(block, 'y', javascript.Order.ATOMIC);
var dropdown_color = block.getFieldValue('color');
var code = `${variable_oled_hand}.drawPixel([[${value_x}, ${value_y}, ${dropdown_color}]]);\n`;
return code;
};
/********* */
/** Flyout */
/********* */
flyout_contents = flyout_contents.concat([
{
"kind": "label",
"text": "有機ELディスプレイ(SSD1306)",
"web-line": "4.0",
"web-line-width": "200"
},
{
"kind": "block",
"type": "oc_oled_init",
"fields": {
"type": "field_variable",
"name": "i2c_device",
"variable": "oled"
},
"fields": {
"i2c_addr": "0x3c",
"disp_size": "128x64"
}
},
{
"kind": "block",
"type": "oc_oled_drawpixel",
"fields": {
"type": "field_variable",
"name": "oled_hand",
"variable": "oled"
},
"inputs": {
"x": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "0"
}
}
},
"y": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "0"
}
}
},
},
"fields": {
"color": "1",
}
},
{
"kind": "block",
"type": "oc_oled_drawline",
"fields": {
"type": "field_variable",
"name": "oled_hand",
"variable": "oled"
},
"inputs": {
"start_x": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "0"
}
}
},
"start_y": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "0"
}
}
},
"end_x": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "128"
}
}
},
"end_y": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "64"
}
}
},
},
"fields": {
"color": "1",
}
},
{
"kind": "block",
"type": "oc_oled_fillrect",
"fields": {
"type": "field_variable",
"name": "oled_hand",
"variable": "oled"
},
"inputs": {
"left": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "24"
}
}
},
"top": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "12"
}
}
},
"width": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "80"
}
}
},
"height": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "40"
}
}
},
},
"fields": {
"color": "1",
}
},
{
"kind": "block",
"type": "oc_oled_canvastext",
"fields": {
"type": "field_variable",
"name": "oled_hand",
"variable": "oled"
},
"inputs": {
"text": {
"shadow": {
"type": "text",
"fields": {
"TEXT": "我輩は猫である。"
}
}
},
"start_x": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "0"
}
}
},
"start_y": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "0"
}
}
},
},
"fields": {
"font": '8px MisakiGothic',
"color": "white"
}
},
{
"kind": "block",
"type": "oc_oled_writestring",
"fields": {
"type": "field_variable",
"name": "oled_hand",
"variable": "oled"
},
"inputs": {
"text": {
"shadow": {
"type": "text",
"fields": {
"TEXT": "I am a cat."
}
}
},
"start_x": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "0"
}
}
},
"start_y": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "0"
}
}
},
},
"fields": {
"font": 'oled_5x7',
"color": "1"
}
}
]);

Binary file not shown.

Binary file not shown.

BIN
fonts/misaki_gothic.ttf Normal file

Binary file not shown.

BIN
img/oled_prog_bw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

View File

@ -8,8 +8,10 @@ const testfunc = async () => {
//============ User Customize Start =============== //============ User Customize Start ===============
// カスタムブロックカラー定義 // カスタムブロックカラー定義
Blockly.HSV_SATURATION = 0.55; Blockly.utils.colour.setHsvSaturation(0.55);
Blockly.HSV_VALUE = 0.75; Blockly.utils.colour.setHsvValue(0.75);
// Blockly.HSV_SATURATION = 0.55;
// Blockly.HSV_VALUE = 0.75;
var gpio_color = '0'; var gpio_color = '0';
var sonsor_color = '20'; var sonsor_color = '20';
var multimedia_color = '240'; var multimedia_color = '240';

View File

@ -0,0 +1,58 @@
'use strict';
const err_msg = 'AMG8833 is already opened. Please close old connection to use new one.';
// const pig = require(`${apptool.gpio_lib}`);
// let pi = -1;
let rg;
let i2c_hand = -1;
exports.init = async (_rg, 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);
rg = _rg;
if (i2c_hand >= 0) { throw new Error(err_msg); return; }
i2c_hand = await rg.i2c_open(i2c_bus, i2c_addr, 0);
// i2c_hand = await pig._i2c_open(pi, i2c_bus, i2c_addr, 0);
console.log('i2c_hand=' + i2c_hand);
await rg.i2c_write_byte_data(i2c_hand, 0x00, 0x00); //Normal mode
await rg.i2c_write_byte_data(i2c_hand, 0x02, 0x00); //10FPS
}
exports.read_thermistor = async () => {
let temp = await rg.i2c_read_word_data(i2c_hand, 0x0e);
return temp * 0.0625;
}
exports.read_temp_array = async () => {
let linedata = [];
for (let i = 0; i < 8; i++) {
let data = await rg.i2c_read_i2c_block_data(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 rg.i2c_close(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
*/

View File

@ -0,0 +1,7 @@
{
"name": "@ocoge/amg8833",
"version": "1.0.0",
"main": "amg8833x.js",
"private": true,
"license": "MIT"
}

View File

@ -1,7 +1,8 @@
'use strict'; 'use strict';
this.pig = null; // this.pig = null;
this.pi = null; // this.pi = null;
this.rg = null;
this.i2cBusNo = null; this.i2cBusNo = null;
this.i2cAddress = null; this.i2cAddress = null;
@ -41,18 +42,19 @@ this.REGISTER_PRESSURE_DATA = 0xF7;
this.REGISTER_TEMP_DATA = 0xFA; this.REGISTER_TEMP_DATA = 0xFA;
this.REGISTER_HUMIDITY_DATA = 0xFD; this.REGISTER_HUMIDITY_DATA = 0xFD;
exports.init = async (options) => { exports.init = async (rg, options) => {
this.pig = require(`${apptool.gpio_lib}`); // this.pig = require(`${apptool.gpio_lib}`);
this.pi = await this.pig._rgpiod_start('', ''); // this.pi = await this.rg.rgpiod_start('', '');
this.rg = rg;
this.i2cBusNo = (options && options.hasOwnProperty('i2cBusNo')) ? options.i2cBusNo : 1; this.i2cBusNo = (options && options.hasOwnProperty('i2cBusNo')) ? options.i2cBusNo : 1;
this.i2cAddress = (options && options.hasOwnProperty('i2cAddress')) ? options.i2cAddress : this.BME280_DEFAULT_I2C_ADDRESS(); 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, 0); this.i2cHand = await this.rg.i2c_open(this.i2cBusNo, this.i2cAddress, 0);
let r; let r;
r = await this.pig._i2c_write_byte_data(this.pi, this.i2cHand, this.REGISTER_CHIPID, 0); r = await this.rg.i2c_write_byte_data(this.i2cHand, this.REGISTER_CHIPID, 0);
if (r < 0) return r; if (r < 0) return r;
let chipId = await this.pig._i2c_read_byte_data(this.pi, this.i2cHand, this.REGISTER_CHIPID); let chipId = await this.rg.i2c_read_byte_data(this.i2cHand, this.REGISTER_CHIPID);
if (chipId !== this.CHIP_ID_BME280() && if (chipId !== this.CHIP_ID_BME280() &&
chipId !== this.CHIP_ID1_BMP280() && chipId !== this.CHIP_ID1_BMP280() &&
chipId !== this.CHIP_ID2_BMP280() && chipId !== this.CHIP_ID2_BMP280() &&
@ -66,11 +68,11 @@ exports.init = async (options) => {
} }
// Humidity 16x oversampling // Humidity 16x oversampling
// //
let r = await this.pig._i2c_write_byte_data(this.pi, this.i2cHand, this.REGISTER_CONTROL_HUM, 0b00000101); let r = await this.rg.i2c_write_byte_data(this.i2cHand, this.REGISTER_CONTROL_HUM, 0b00000101);
if (r < 0) return `Humidity 16x oversampling error: ${r}`; if (r < 0) return `Humidity 16x oversampling error: ${r}`;
// Temperture/pressure 16x oversampling, normal mode // Temperture/pressure 16x oversampling, normal mode
// //
r = await this.pig._i2c_write_byte_data(this.pi, this.i2cHand, this.REGISTER_CONTROL, 0b10110111); r = await this.rg.i2c_write_byte_data(this.i2cHand, this.REGISTER_CONTROL, 0b10110111);
if (r < 0) return `Temperture/pressure 16x oversampling error: ${r}`; if (r < 0) return `Temperture/pressure 16x oversampling error: ${r}`;
return 0; return 0;
@ -83,7 +85,7 @@ exports.init = async (options) => {
// //
exports.reset = async () => { exports.reset = async () => {
const POWER_ON_RESET_CMD = 0xB6; 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); let r = await this.rg.i2c_write_byte_data(this.i2cHand, this.REGISTER_RESET, POWER_ON_RESET_CMD);
if (r < 0) return `cannot power-on reset: ${r}`; if (r < 0) return `cannot power-on reset: ${r}`;
else return 0; else return 0;
} }
@ -94,10 +96,10 @@ exports.reset = async () => {
// //
exports.cancel = async () => { exports.cancel = async () => {
if (this.i2cHand >= 0) { if (this.i2cHand >= 0) {
await this.pig._i2c_close(this.pi, this.i2cHand); await this.rg.i2c_close(this.i2cHand);
this.i2cHand = null; this.i2cHand = null;
await this.pig._rgpiod_stop(this.pi); // await this.rg.rgpiod_stop(this.pi);
this.pi = null; // this.pi = null;
} }
} }
@ -108,7 +110,7 @@ exports.readSensorData = async () => {
// Grab temperature, humidity, and pressure in a single read // 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); let buffer = await this.rg.i2c_read_i2c_block_data(this.i2cHand, this.REGISTER_PRESSURE_DATA, 8);
if (!buffer) return `couldn't grab data`; if (!buffer) return `couldn't grab data`;
// Temperature (temperature first since we need t_fine for pressure and humidity) // Temperature (temperature first since we need t_fine for pressure and humidity)
// //
@ -160,15 +162,15 @@ exports.readSensorData = async () => {
} }
exports.loadCalibration = async (callback) => { exports.loadCalibration = async (callback) => {
let buffer = await this.pig._i2c_read_i2c_block_data(this.pi, this.i2cHand, this.REGISTER_DIG_T1, 24); let buffer = await this.rg.i2c_read_i2c_block_data(this.i2cHand, this.REGISTER_DIG_T1, 24);
if (buffer) { if (buffer) {
let h1 = await this.pig._i2c_read_byte_data(this.pi, this.i2cHand, this.REGISTER_DIG_H1); let h1 = await this.rg.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H1);
let h2 = await this.pig._i2c_read_word_data(this.pi, this.i2cHand, this.REGISTER_DIG_H2); let h2 = await this.rg.i2c_read_word_data(this.i2cHand, this.REGISTER_DIG_H2);
let h3 = await this.pig._i2c_read_byte_data(this.pi, this.i2cHand, this.REGISTER_DIG_H3); let h3 = await this.rg.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H3);
let h4 = await this.pig._i2c_read_byte_data(this.pi, this.i2cHand, this.REGISTER_DIG_H4); let h4 = await this.rg.i2c_read_byte_data(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 = await this.rg.i2c_read_byte_data(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 h5_1 = await this.rg.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H5 + 1);
let h6 = await this.pig._i2c_read_byte_data(this.pi, this.i2cHand, this.REGISTER_DIG_H6); let h6 = await this.rg.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H6);
this.cal = { this.cal = {
dig_T1: this.uint16(buffer[1], buffer[0]), dig_T1: this.uint16(buffer[1], buffer[0]),

View File

@ -0,0 +1,7 @@
{
"name": "@ocoge/bme280",
"version": "1.0.0",
"main": "bme280x.js",
"private": true,
"license": "MIT"
}

View File

@ -0,0 +1,7 @@
{
"name": "@ocoge/paj7620",
"version": "1.0.0",
"main": "paj7620x.js",
"private": true,
"license": "MIT"
}

View File

@ -313,22 +313,23 @@ const debug = 1;
const err_msg = 'PAJ7620 is already opened. Please close old connection to use new one.'; const err_msg = 'PAJ7620 is already opened. Please close old connection to use new one.';
let rg = -1; let rg = -1;
let sbc = -1; // let sbc = -1;
let i2c_hand = -1; let i2c_hand = -1;
//Initialize the sensors //Initialize the sensors
exports.init = async (i2c_bus, i2c_addr, wael = null) => { exports.init = async (_rg, i2c_bus, i2c_addr) => {
if (wael !== null) { // if (wael !== null) {
wael('beforeunload', async () => { // wael('beforeunload', async () => {
await exports.stop(); // await exports.stop();
}); // });
} // }
rg = require(`rgpio`);//${apptool.gpio_lib} // rg = require(`rgpio`);//${apptool.gpio_lib}
if (sbc >= 0) { throw new Error(err_msg); return; } // if (sbc >= 0) { throw new Error(err_msg); return; }
sbc = await rg._rgpiod_start('', ''); // sbc = await rg._rgpiod_start('', '');
rg = _rg;
if (i2c_hand >= 0) { throw new Error(err_msg); return; } if (i2c_hand >= 0) { throw new Error(err_msg); return; }
i2c_hand = await rg._i2c_open(sbc, i2c_bus, i2c_addr, 0); i2c_hand = await rg.i2c_open(i2c_bus, i2c_addr, 0);
if (debug) if (debug)
console.log("sbc=" + sbc + ", i2c_hand=" + i2c_hand); console.log("i2c_hand=" + i2c_hand);
await sleep(.001); await sleep(.001);
await paj7620SelectBank(BANK0); await paj7620SelectBank(BANK0);
@ -355,7 +356,7 @@ exports.init = async (i2c_bus, i2c_addr, wael = null) => {
// Write a byte to a register on the Gesture sensor // Write a byte to a register on the Gesture sensor
const paj7620WriteReg = async (addr, cmd) => const paj7620WriteReg = async (addr, cmd) =>
await rg._i2c_write_word_data(sbc, i2c_hand, addr, cmd); await rg.i2c_write_word_data(i2c_hand, addr, cmd);
//Select a register bank on the Gesture Sensor //Select a register bank on the Gesture Sensor
const paj7620SelectBank = async bank => { const paj7620SelectBank = async bank => {
@ -365,7 +366,7 @@ const paj7620SelectBank = async bank => {
//Read a block of bytes of length "qty" starting at address "addr" from the Gesture sensor //Read a block of bytes of length "qty" starting at address "addr" from the Gesture sensor
const paj7620ReadReg = async (addr, qty) => { const paj7620ReadReg = async (addr, qty) => {
return await rg._i2c_read_i2c_block_data(sbc, i2c_hand, addr, qty); return await rg.i2c_read_i2c_block_data(i2c_hand, addr, qty);
} }
//Return a vlaue from the gestire sensor which can be used in a program //Return a vlaue from the gestire sensor which can be used in a program
@ -457,13 +458,13 @@ exports.return_gesture = async () => {
exports.stop = async () => { exports.stop = async () => {
if (i2c_hand >= 0) { if (i2c_hand >= 0) {
await rg._i2c_close(sbc, i2c_hand); await rg.i2c_close(i2c_hand);
i2c_hand = -1; i2c_hand = -1;
} }
if (sbc >= 0) { // if (sbc >= 0) {
await rg._rgpiod_stop(sbc); // await rg.rgpiod_stop(sbc);
sbc = -1; // sbc = -1;
} // }
} }
/* /*

View File

@ -94,12 +94,18 @@ module.exports.i2c_write_byte_data = async (handle, reg, byte_val) => {
module.exports.i2c_read_byte_data = async (handle, reg) => { module.exports.i2c_read_byte_data = async (handle, reg) => {
return await module.exports._i2c_read_byte_data(sbc, handle, reg); return await module.exports._i2c_read_byte_data(sbc, handle, reg);
} }
module.exports.i2c_read_i2c_block_data = async (handle, reg, count = -1) => {
return await module.exports._i2c_read_i2c_block_data(sbc, handle, reg, count);
}
module.exports.i2c_write_i2c_block_data = async (handle, reg, data) => { module.exports.i2c_write_i2c_block_data = async (handle, reg, data) => {
return await module.exports._i2c_write_i2c_block_data(sbc, handle, reg, Buffer.from(data), -1); return await module.exports._i2c_write_i2c_block_data(sbc, handle, reg, Buffer.from(data), -1);
} }
module.exports.i2c_read_word_data = async (handle, reg) => { module.exports.i2c_read_word_data = async (handle, reg) => {
return await module.exports._i2c_read_word_data(sbc, handle, reg); return await module.exports._i2c_read_word_data(sbc, handle, reg);
} }
module.exports.i2c_write_word_data = async (handle, reg, word_val) => {
return await module.exports._i2c_write_word_data(sbc, handle, reg, word_val);
}
module.exports.i2c_read_device = async (handle, count) => { module.exports.i2c_read_device = async (handle, count) => {
return new TextDecoder().decode(await module.exports._i2c_read_device(sbc, handle, count)); return new TextDecoder().decode(await module.exports._i2c_read_device(sbc, handle, count));
} }
@ -107,6 +113,37 @@ module.exports.i2c_write_device = async (handle, data) => {
return await module.exports._i2c_write_device(sbc, handle, Buffer.from(data), -1); return await module.exports._i2c_write_device(sbc, handle, Buffer.from(data), -1);
} }
/***** 同期関数 *****/
module.exports.rgpio_sbc_sync = (host = 'localhost', port = 8889, show_errors = true) => {
if (sbc < 0) {
sbc = module.exports._rgpiod_start_sync(host, port.toString());
if (sbc < 0) {
if (show_errors) console.log(sbc);
}
}
return sbc;
}
module.exports.sbc_stop_sync = () => {
module.exports._rgpiod_stop_sync(sbc);
chip_hand = [];
sbc = -1;
}
module.exports.i2c_open_sync = (i2c_bus, i2c_address, i2c_flags = 0) => {
return module.exports._i2c_open_sync(sbc, i2c_bus, i2c_address, i2c_flags);
}
module.exports.i2c_close_sync = handle => {
module.exports._i2c_close_sync(sbc, handle);
}
module.exports.i2c_read_byte_sync = handle => {
return module.exports._i2c_read_byte_sync(sbc, handle);
}
module.exports.i2c_write_device_sync = (handle, data, count = -1) => {
let buffer = Buffer.from(data);
if (count < 0) count = buffer.length;
return module.exports._i2c_write_device_sync(sbc, handle, buffer, count);
}
// 終了処理 // 終了処理
module.exports.close_all_handle = async () => { module.exports.close_all_handle = async () => {
await module.exports.sbc_stop(); await module.exports.sbc_stop();

View File

@ -1,5 +1,5 @@
{ {
"name": "rgpio", "name": "@ocoge/rgpio",
"version": "0.0.1", "version": "0.0.1",
"main": "index.js", "main": "index.js",
"private": true, "private": true,

View File

@ -652,12 +652,156 @@ Promise _i2cWriteDevice(const CallbackInfo &info)
int count = info[3].As<Number>().Uint32Value(); int count = info[3].As<Number>().Uint32Value();
if (count < 0) if (count < 0)
int count = buf.Length(); count = buf.Length();
deferred.Resolve(Number::New(env, i2c_write_device(sbc, handle, buf.Data(), count))); deferred.Resolve(Number::New(env, i2c_write_device(sbc, handle, buf.Data(), count)));
} }
return deferred.Promise(); return deferred.Promise();
} }
/******** 同期処理関数 ********/
// rgpio デーモンに接続
Value _rgpiodStartSync(const CallbackInfo &info)
{
Env env = info.Env();
if (info.Length() != 2)
{
TypeError::New(env, "Invalid argument count: _rgpiodStartSync")
.ThrowAsJavaScriptException();
return env.Null();
}
else if (!info[0].IsString() || !info[1].IsString())
{
TypeError::New(env, "Invalid argument types: _rgpiodStartSync").ThrowAsJavaScriptException();
return env.Null();
}
else
{
std::string ipaddr = info[0].As<String>().Utf8Value();
std::string port = info[1].As<String>().Utf8Value();
return Number::New(env, rgpiod_start(ipaddr.c_str(), port.c_str()));
}
}
// rgpioデーモンとの接続を閉じる
Value _rgpiodStopSync(const CallbackInfo &info)
{
Env env = info.Env();
if (info.Length() != 1)
{
TypeError::New(env, "Invalid argument count: _rgpiodStopSync")
.ThrowAsJavaScriptException();
return env.Null();
}
else if (!info[0].IsNumber())
{
TypeError::New(env, "Invalid argument types: _rgpiodStop")
.ThrowAsJavaScriptException();
return env.Null();
}
else
{
int sbc = info[0].As<Number>().Int32Value();
rgpiod_stop(sbc);
return env.Null();
}
}
// I2Cバスアドレスのデバイスのハンドルを返す
Value _i2cOpenSync(const CallbackInfo &info)
{
Env env = info.Env();
if (info.Length() != 4)
{
TypeError::New(env, "Invalid argument count: _i2cOpen")
.ThrowAsJavaScriptException();
return env.Null();
}
else if (!info[0].IsNumber() || !info[1].IsNumber() || !info[2].IsNumber() || !info[3].IsNumber())
{
TypeError::New(env, "Invalid argument types: _i2cOpen")
.ThrowAsJavaScriptException();
return env.Null();
}
else
{
int sbc = info[0].As<Number>().Int32Value();
int i2c_bus = info[1].As<Number>().Uint32Value();
int i2c_addr = info[2].As<Number>().Uint32Value();
int i2c_flags = info[3].As<Number>().Uint32Value();
return Number::New(env, i2c_open(sbc, i2c_bus, i2c_addr, i2c_flags));
}
}
// オープン済みI2Cハンドルを閉じる
Value _i2cCloseSync(const CallbackInfo &info)
{
Env env = info.Env();
if (info.Length() != 2)
{
TypeError::New(env, "Invalid argument count: _i2cClose")
.ThrowAsJavaScriptException();
return env.Null();
}
else if (!info[0].IsNumber() || !info[1].IsNumber())
{
TypeError::New(env, "Invalid argument types: _i2cClose")
.ThrowAsJavaScriptException();
return env.Null();
}
else
{
int sbc = info[0].As<Number>().Int32Value();
int handle = info[1].As<Number>().Uint32Value();
return Number::New(env, i2c_close(sbc, handle));
}
}
// デバイスから1バイトを受け取る
Value _i2cReadByteSync(const CallbackInfo &info)
{
Env env = info.Env();
if (info.Length() != 2)
{
TypeError::New(env, "Invalid argument count: _i2cReadByteSync")
.ThrowAsJavaScriptException();
return env.Null();
}
else if (!info[0].IsNumber() || !info[0].IsNumber())
{
TypeError::New(env, "Invalid argument types: _i2cReadByteSync")
.ThrowAsJavaScriptException();
return env.Null();
}
else
{
int sbc = info[0].As<Number>().Int32Value();
int handle = info[1].As<Number>().Uint32Value();
return Number::New(env, i2c_read_byte(sbc, handle));
}
}
// i2c デバイスにバイト列を送る(data: buffer)
Value _i2cWriteDeviceSync(const CallbackInfo &info)
{
Env env = info.Env();
if (info.Length() != 4)
{
TypeError::New(env, "Invalid argument count: _i2cWriteDevice")
.ThrowAsJavaScriptException();
return env.Null();
}
else if (!info[0].IsNumber() || !info[1].IsNumber() || !info[2].IsBuffer() || !info[3].IsNumber())
{
TypeError::New(env, "Invalid argument types: _i2cWriteDevice")
.ThrowAsJavaScriptException();
return env.Null();
}
else
{
int sbc = info[0].As<Number>().Int32Value();
int handle = info[1].As<Number>().Uint32Value();
auto buf = info[2].As<Buffer<char>>();
int count = info[3].As<Number>().Uint32Value();
return Number::New(env, i2c_write_device(sbc, handle, buf.Data(), count));
}
}
Object Object
Init(Env env, Object exports) Init(Env env, Object exports)
{ {
@ -686,6 +830,13 @@ Init(Env env, Object exports)
exports.Set(String::New(env, "_i2c_write_word_data"), Function::New(env, _i2cWriteWordData)); exports.Set(String::New(env, "_i2c_write_word_data"), Function::New(env, _i2cWriteWordData));
exports.Set(String::New(env, "_i2c_write_device"), Function::New(env, _i2cWriteDevice)); exports.Set(String::New(env, "_i2c_write_device"), Function::New(env, _i2cWriteDevice));
exports.Set(String::New(env, "_i2c_read_device"), Function::New(env, _i2cReadDevice)); exports.Set(String::New(env, "_i2c_read_device"), Function::New(env, _i2cReadDevice));
exports.Set(String::New(env, "_rgpiod_start_sync"), Function::New(env, _rgpiodStartSync));
exports.Set(String::New(env, "_rgpiod_stop_sync"), Function::New(env, _rgpiodStopSync));
exports.Set(String::New(env, "_i2c_open_sync"), Function::New(env, _i2cOpenSync));
exports.Set(String::New(env, "_i2c_close_sync"), Function::New(env, _i2cCloseSync));
exports.Set(String::New(env, "_i2c_read_byte_sync"), Function::New(env, _i2cReadByteSync));
exports.Set(String::New(env, "_i2c_write_device_sync"), Function::New(env, _i2cWriteDeviceSync));
return exports; return exports;
} }

View File

@ -0,0 +1,669 @@
var Oled = function (rg, opts) {
this.HEIGHT = opts.height || 32;
this.WIDTH = opts.width || 128;
this.ADDRESS = opts.address || 0x3C;
this.BUS = opts.bus || 1;
this.PROTOCOL = 'I2C';
this.LINESPACING = typeof opts.linespacing !== 'undefined' ? opts.linespacing : 1;
this.LETTERSPACING = typeof opts.letterspacing !== 'undefined' ? opts.letterspacing : 1;
// create command buffers
this.DISPLAY_OFF = 0xAE;
this.DISPLAY_ON = 0xAF;
this.SET_DISPLAY_CLOCK_DIV = 0xD5;
this.SET_MULTIPLEX = 0xA8;
this.SET_DISPLAY_OFFSET = 0xD3;
this.SET_START_LINE = 0x00;
this.CHARGE_PUMP = 0x8D;
this.EXTERNAL_VCC = false;
this.MEMORY_MODE = 0x20;
this.SEG_REMAP = 0xA1; // using 0xA0 will flip screen
this.COM_SCAN_DEC = 0xC8;
this.COM_SCAN_INC = 0xC0;
this.SET_COM_PINS = 0xDA;
this.SET_CONTRAST = 0x81;
this.SET_PRECHARGE = 0xd9;
this.SET_VCOM_DETECT = 0xDB;
this.DISPLAY_ALL_ON_RESUME = 0xA4;
this.NORMAL_DISPLAY = 0xA6;
this.COLUMN_ADDR = 0x21;
this.PAGE_ADDR = 0x22;
this.INVERT_DISPLAY = 0xA7;
this.ACTIVATE_SCROLL = 0x2F;
this.DEACTIVATE_SCROLL = 0x2E;
this.SET_VERTICAL_SCROLL_AREA = 0xA3;
this.RIGHT_HORIZONTAL_SCROLL = 0x26;
this.LEFT_HORIZONTAL_SCROLL = 0x27;
this.VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29;
this.VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A;
this.cursor_x = 0;
this.cursor_y = 0;
// new blank buffer
this.buffer = Buffer.alloc((this.WIDTH * this.HEIGHT) / 8);
this.buffer.fill(0x00);
this.dirtyBytes = [];
var config = {
'128x32': {
'multiplex': 0x1F,
'compins': 0x02,
'coloffset': 0
},
'128x64': {
'multiplex': 0x3F,
'compins': 0x12,
'coloffset': 0
},
'96x16': {
'multiplex': 0x0F,
'compins': 0x2,
'coloffset': 0,
}
};
// Setup i2c
this.rg = rg;
this.rg.rgpio_sbc_sync();
this.i2c_hand = this.rg.i2c_open_sync(this.BUS, this.ADDRESS);
// console.log(`this.i2c_hand = ${this.i2c_hand}`);
var screenSize = this.WIDTH + 'x' + this.HEIGHT;
this.screenConfig = config[screenSize];
this._initialise();
}
Oled.prototype._initialise = function () {
// sequence of bytes to initialise with
var initSeq = [
this.DISPLAY_OFF,
this.SET_DISPLAY_CLOCK_DIV, 0x80,
this.SET_MULTIPLEX, this.screenConfig.multiplex, // set the last value dynamically based on screen size requirement
this.SET_DISPLAY_OFFSET, 0x00, // sets offset pro to 0
this.SET_START_LINE,
this.CHARGE_PUMP, 0x14, // charge pump val
this.MEMORY_MODE, 0x00, // 0x0 act like ks0108
this.SEG_REMAP, // screen orientation
this.COM_SCAN_DEC, // screen orientation change to INC to flip
this.SET_COM_PINS, this.screenConfig.compins, // com pins val sets dynamically to match each screen size requirement
this.SET_CONTRAST, 0x8F, // contrast val
this.SET_PRECHARGE, 0xF1, // precharge val
this.SET_VCOM_DETECT, 0x40, // vcom detect
this.DISPLAY_ALL_ON_RESUME,
this.NORMAL_DISPLAY,
this.DISPLAY_ON
];
var i, initSeqLen = initSeq.length;
// write init seq commands
for (i = 0; i < initSeqLen; i++) {
this._transfer('cmd', initSeq[i]);
}
}
// writes both commands and data buffers to this device
Oled.prototype._transfer = function (type, val, fn) {
var control;
if (type === 'data') {
control = 0x40;
} else if (type === 'cmd') {
control = 0x00;
} else {
return;
}
var bufferForSend;
bufferForSend = Buffer.from([control, val])
// send control and actual val
this.rg.i2c_write_device_sync(this.i2c_hand, bufferForSend, 2);
if (fn) {
fn();
}
}
// read a byte from the oled
Oled.prototype._readI2C = function (fn) {
var data = this.rg.i2c_read_byte_sync(this.i2c_hand);
fn(data);
}
// sometimes the oled gets a bit busy with lots of bytes.
// Read the response byte to see if this is the case
Oled.prototype._waitUntilReady = function (callback) {
var done,
oled = this;
function tick(callback) {
oled._readI2C(function (byte) {
// read the busy byte in the response
busy = byte >> 7 & 1;
if (!busy) {
// if not busy, it's ready for callback
callback();
} else {
setTimeout(function () { tick(callback) }, 0);
}
});
};
setTimeout(function () { tick(callback) }, 0);
}
// set starting position of a text string on the oled
Oled.prototype.setCursor = function (x, y) {
this.cursor_x = x;
this.cursor_y = y;
}
// write text to the oled
Oled.prototype.writeString = function (font, size, string, color, wrap, sync) {
var immed = (typeof sync === 'undefined') ? true : sync;
var wordArr = string.split(' '),
len = wordArr.length,
// start x offset at cursor pos
offset = this.cursor_x,
padding = 0;
// loop through words
for (var w = 0; w < len; w += 1) {
// put the word space back in for all in between words or empty words
if (w < len - 1 || !wordArr[w].length) {
wordArr[w] += ' ';
}
var stringArr = wordArr[w].split(''),
slen = stringArr.length,
compare = (font.width * size * slen) + (size * (len - 1));
// wrap words if necessary
if (wrap && len > 1 && w > 0 && (offset >= (this.WIDTH - compare))) {
offset = 0;
this.cursor_y += (font.height * size) + this.LINESPACING;
this.setCursor(offset, this.cursor_y);
}
// loop through the array of each char to draw
for (var i = 0; i < slen; i += 1) {
if (stringArr[i] === '\n') {
offset = 0;
this.cursor_y += (font.height * size) + this.LINESPACING;
this.setCursor(offset, this.cursor_y);
}
else {
// look up the position of the char, pull out the buffer slice
var charBuf = this._findCharBuf(font, stringArr[i]);
// read the bits in the bytes that make up the char
var charBytes = this._readCharBytes(charBuf, font.height);
// draw the entire character
this._drawChar(charBytes, font.height, size, false);
// calc new x position for the next char, add a touch of padding too if it's a non space char
//padding = (stringArr[i] === ' ') ? 0 : this.LETTERSPACING;
offset += (font.width * size) + this.LETTERSPACING;// padding;
// wrap letters if necessary
if (wrap && (offset >= (this.WIDTH - font.width - this.LETTERSPACING))) {
offset = 0;
this.cursor_y += (font.height * size) + this.LINESPACING;
}
// set the 'cursor' for the next char to be drawn, then loop again for next char
this.setCursor(offset, this.cursor_y);
}
}
}
if (immed) {
this._updateDirtyBytes(this.dirtyBytes);
}
}
// draw an individual character to the screen
Oled.prototype._drawChar = function (byteArray, charHeight, size, sync) {
// take your positions...
var x = this.cursor_x,
y = this.cursor_y;
// loop through the byte array containing the hexes for the char
for (var i = 0; i < byteArray.length; i += 1) {
for (var j = 0; j < charHeight; j += 1) {
// pull color out
var color = byteArray[i][j],
xpos, ypos;
// standard font size
if (size === 1) {
xpos = x + i;
ypos = y + j;
this.drawPixel([xpos, ypos, color], false);
} else {
// MATH! Calculating pixel size multiplier to primitively scale the font
xpos = x + (i * size);
ypos = y + (j * size);
this.fillRect(xpos, ypos, size, size, color, false);
}
}
}
}
// get character bytes from the supplied font object in order to send to framebuffer
Oled.prototype._readCharBytes = function (byteArray, charHeight) {
var bitArr = [],
bitCharArr = [];
// loop through each byte supplied for a char
for (var i = 0; i < byteArray.length; i += 1) {
// set current byte
var byte = byteArray[i];
// read each byte
for (var j = 0; j < charHeight; j += 1) {
// shift bits right until all are read
var bit = byte >> j & 1;
bitArr.push(bit);
}
// push to array containing flattened bit sequence
bitCharArr.push(bitArr);
// clear bits for next byte
bitArr = [];
}
return bitCharArr;
}
// find where the character exists within the font object
Oled.prototype._findCharBuf = function (font, c) {
// use the lookup array as a ref to find where the current char bytes start
var cBufPos = font.lookup.indexOf(c) * font.width;
// slice just the current char's bytes out of the fontData array and return
var cBuf = font.fontData.slice(cBufPos, cBufPos + font.width);
return cBuf;
}
// send the entire framebuffer to the oled
Oled.prototype.update = function () {
// wait for oled to be ready
this._waitUntilReady(function () {
// set the start and endbyte locations for oled display update
var displaySeq = [
this.COLUMN_ADDR,
this.screenConfig.coloffset,
this.screenConfig.coloffset + this.WIDTH - 1, // column start and end address
this.PAGE_ADDR, 0, (this.HEIGHT / 8) - 1 // page start and end address
];
var displaySeqLen = displaySeq.length,
bufferLen = this.buffer.length,
i, v;
// send intro seq
for (i = 0; i < displaySeqLen; i += 1) {
this._transfer('cmd', displaySeq[i]);
}
// write buffer data
var bufferToSend = Buffer.concat([Buffer.from([0x40]), this.buffer]);
this.rg.i2c_write_device_sync(this.i2c_hand, bufferToSend, bufferToSend.length);
}.bind(this));
}
// send dim display command to oled
Oled.prototype.dimDisplay = function (bool) {
var contrast;
if (bool) {
contrast = 0; // Dimmed display
} else {
contrast = 0xCF; // Bright display
}
this._transfer('cmd', this.SET_CONTRAST);
this._transfer('cmd', contrast);
}
// turn oled off
Oled.prototype.turnOffDisplay = function () {
this._transfer('cmd', this.DISPLAY_OFF);
}
// turn oled on
Oled.prototype.turnOnDisplay = function () {
this._transfer('cmd', this.DISPLAY_ON);
}
// clear all pixels currently on the display
Oled.prototype.clearDisplay = function (sync) {
var immed = (typeof sync === 'undefined') ? true : sync;
// write off pixels
this.buffer.fill(0x00);
if (immed) {
this.update();
}
}
// invert pixels on oled
Oled.prototype.invertDisplay = function (bool) {
if (bool) {
this._transfer('cmd', this.INVERT_DISPLAY); // inverted
} else {
this._transfer('cmd', this.NORMAL_DISPLAY); // non inverted
}
}
// draw an RGBA image at the specified coordinates
Oled.prototype.drawRGBAImage = function (image, dx, dy, sync) {
var immed = (typeof sync === 'undefined') ? true : sync;
// translate image data to buffer
var x, y, dataIndex, buffIndex, buffByte, bit, pixelByte;
var dyp = this.WIDTH * Math.floor(dy / 8); // calc once
var dxyp = dyp + dx;
for (x = 0; x < image.width; x++) {
var dxx = dx + x;
if (dxx < 0 || dxx >= this.WIDTH) {
// negative, off the screen
continue;
}
// start buffer index for image column
buffIndex = x + dxyp;
buffByte = this.buffer[buffIndex];
for (y = 0; y < image.height; y++) {
var dyy = dy + y; // calc once
if (dyy < 0 || dyy >= this.HEIGHT) {
// negative, off the screen
continue;
}
var dyyp = Math.floor(dyy / 8); // calc once
// check if start of buffer page
if (!(dyy % 8)) {
// check if we need to save previous byte
if ((x || y) && buffByte !== this.buffer[buffIndex]) {
// save current byte and get next buffer byte
this.buffer[buffIndex] = buffByte;
this.dirtyBytes.push(buffIndex);
}
// new buffer page
buffIndex = dx + x + this.WIDTH * dyyp;
buffByte = this.buffer[buffIndex];
}
// process pixel into buffer byte
dataIndex = (image.width * y + x) << 2; // 4 bytes per pixel (RGBA)
if (!image.data[dataIndex + 3]) {
// transparent, continue to next pixel
continue;
}
pixelByte = 0x01 << (dyy - 8 * dyyp);
bit = image.data[dataIndex] || image.data[dataIndex + 1] || image.data[dataIndex + 2];
if (bit) {
buffByte |= pixelByte;
}
else {
buffByte &= ~pixelByte;
}
}
if ((x || y) && buffByte !== this.buffer[buffIndex]) {
// save current byte
this.buffer[buffIndex] = buffByte;
this.dirtyBytes.push(buffIndex);
}
}
if (immed) {
this._updateDirtyBytes(this.dirtyBytes);
}
}
// draw an image pixel array on the screen
Oled.prototype.drawBitmap = function (pixels, sync) {
var immed = (typeof sync === 'undefined') ? true : sync;
var x, y,
pixelArray = [];
for (var i = 0; i < pixels.length; i++) {
x = Math.floor(i % this.WIDTH);
y = Math.floor(i / this.WIDTH);
this.drawPixel([x, y, pixels[i]], false);
}
if (immed) {
this._updateDirtyBytes(this.dirtyBytes);
}
}
// draw one or many pixels on oled
Oled.prototype.drawPixel = function (pixels, sync) {
var immed = (typeof sync === 'undefined') ? true : sync;
// handle lazy single pixel case
if (typeof pixels[0] !== 'object') pixels = [pixels];
pixels.forEach(function (el) {
// return if the pixel is out of range
var x = el[0], y = el[1], color = el[2];
if (x >= this.WIDTH || y >= this.HEIGHT) return;
// thanks, Martin Richards.
// I wanna can this, this tool is for devs who get 0 indexes
//x -= 1; y -=1;
var byte = 0,
page = Math.floor(y / 8),
pageShift = 0x01 << (y - 8 * page);
// is the pixel on the first row of the page?
(page == 0) ? byte = x : byte = x + (this.WIDTH * page);
// colors! Well, monochrome.
if (color === 'BLACK' || color === 0) {
this.buffer[byte] &= ~pageShift;
}
if (color === 'WHITE' || color > 0) {
this.buffer[byte] |= pageShift;
}
// push byte to dirty if not already there
if (this.dirtyBytes.indexOf(byte) === -1) {
this.dirtyBytes.push(byte);
}
}, this);
if (immed) {
this._updateDirtyBytes(this.dirtyBytes);
}
}
// looks at dirty bytes, and sends the updated bytes to the display
Oled.prototype._updateDirtyBytes = function (byteArray) {
var blen = byteArray.length, i,
displaySeq = [];
// check to see if this will even save time
if (blen > (this.buffer.length / 7)) {
// just call regular update at this stage, saves on bytes sent
this.update();
// now that all bytes are synced, reset dirty state
this.dirtyBytes = [];
} else {
this._waitUntilReady(function () {
// iterate through dirty bytes
for (var i = 0; i < blen; i += 1) {
var byte = byteArray[i];
var page = Math.floor(byte / this.WIDTH);
var col = Math.floor(byte % this.WIDTH);
var displaySeq = [
this.COLUMN_ADDR, col, col, // column start and end address
this.PAGE_ADDR, page, page // page start and end address
];
var displaySeqLen = displaySeq.length, v;
// send intro seq
for (v = 0; v < displaySeqLen; v += 1) {
this._transfer('cmd', displaySeq[v]);
}
// send byte, then move on to next byte
this._transfer('data', this.buffer[byte]);
this.buffer[byte];
}
}.bind(this));
}
// now that all bytes are synced, reset dirty state
this.dirtyBytes = [];
}
// using Bresenham's line algorithm
Oled.prototype.drawLine = function (x0, y0, x1, y1, color, sync) {
var immed = (typeof sync === 'undefined') ? true : sync;
var dx = Math.abs(x1 - x0), sx = x0 < x1 ? 1 : -1,
dy = Math.abs(y1 - y0), sy = y0 < y1 ? 1 : -1,
err = (dx > dy ? dx : -dy) / 2;
while (true) {
this.drawPixel([x0, y0, color], false);
if (x0 === x1 && y0 === y1) break;
var e2 = err;
if (e2 > -dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; }
}
if (immed) {
this._updateDirtyBytes(this.dirtyBytes);
}
}
// draw a filled rectangle on the oled
Oled.prototype.fillRect = function (x, y, w, h, color, sync) {
var immed = (typeof sync === 'undefined') ? true : sync;
// one iteration for each column of the rectangle
for (var i = x; i < x + w; i += 1) {
// draws a vert line
this.drawLine(i, y, i, y + h - 1, color, false);
}
if (immed) {
this._updateDirtyBytes(this.dirtyBytes);
}
}
// activate scrolling for rows start through stop
Oled.prototype.startScroll = function (dir, start, stop) {
var scrollHeader,
cmdSeq = [];
switch (dir) {
case 'right':
cmdSeq.push(this.RIGHT_HORIZONTAL_SCROLL); break;
case 'left':
cmdSeq.push(this.LEFT_HORIZONTAL_SCROLL); break;
// TODO: left diag and right diag not working yet
case 'left diagonal':
cmdSeq.push(
this.SET_VERTICAL_SCROLL_AREA, 0x00,
this.VERTICAL_AND_LEFT_HORIZONTAL_SCROLL,
this.HEIGHT
);
break;
// TODO: left diag and right diag not working yet
case 'right diagonal':
cmdSeq.push(
this.SET_VERTICAL_SCROLL_AREA, 0x00,
this.VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL,
this.HEIGHT
);
break;
}
this._waitUntilReady(function () {
cmdSeq.push(
0x00, start,
0x00, stop,
// TODO: these need to change when diagonal
0x00, 0xFF,
this.ACTIVATE_SCROLL
);
var i, cmdSeqLen = cmdSeq.length;
for (i = 0; i < cmdSeqLen; i += 1) {
this._transfer('cmd', cmdSeq[i]);
}
}.bind(this));
}
// stop scrolling display contents
Oled.prototype.stopScroll = function () {
this._transfer('cmd', this.DEACTIVATE_SCROLL); // stahp
}
/**
* Draw a circle outline - ported from https://www.npmjs.com/package/oled-ssd1306-i2c/v/1.0.6?activeTab=readme
* This method is ad verbatim translation from the corresponding
* method on the Adafruit GFX library
* https://github.com/adafruit/Adafruit-GFX-Library
*/
Oled.prototype.drawCircle = function (x0, y0, r, color, sync) {
var immed = (typeof sync === 'undefined') ? true : sync;
var f = 1 - r;
var ddF_x = 1;
var ddF_y = -2 * r;
var x = 0;
var y = r;
this.drawPixel(
[[x0, y0 + r, color],
[x0, y0 - r, color],
[x0 + r, y0, color],
[x0 - r, y0, color]],
false
);
while (x < y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
this.drawPixel(
[[x0 + x, y0 + y, color],
[x0 - x, y0 + y, color],
[x0 + x, y0 - y, color],
[x0 - x, y0 - y, color],
[x0 + y, y0 + x, color],
[x0 - y, y0 + x, color],
[x0 + y, y0 - x, color],
[x0 - y, y0 - x, color]],
false
);
}
if (immed) {
this._updateDirtyBytes(this.dirtyBytes);
}
};
module.exports = Oled;
/*
* This code was forked from baltazor's oled-i2c-bus: https://www.npmjs.com/package/oled-i2c-bus
*/

View File

@ -0,0 +1,7 @@
{
"name": "@ocoge/ssd1306",
"version": "1.0.0",
"main": "oled.js",
"private": true,
"license": "MIT"
}

17
main.js
View File

@ -297,3 +297,20 @@ ipcMain.handle('save_dialog', (ev, title, defName, filter) => {
ipcMain.handle('get_app_path', (ev) => { ipcMain.handle('get_app_path', (ev) => {
return app.getAppPath() return app.getAppPath()
}) })
// Draw text on node-canvas : Returns PNG buffer data
const { createCanvas, registerFont } = require('canvas')
const PNG = require('pngjs').PNG
registerFont(path.join(__dirname, 'fonts', 'PixelMplus12-Regular.ttf'), { family: '' })
registerFont(path.join(__dirname, 'fonts', 'PixelMplus10-Regular.ttf'), { family: '' })
registerFont(path.join(__dirname, 'fonts', 'misaki_gothic.ttf'), { family: '' })
ipcMain.handle('text_to_rgba', (ev, text, font, color, start_x, start_y) => {
const canvas = createCanvas(128, 64)
const ctx = canvas.getContext('2d')
ctx.antialias = 'none'
ctx.font = font
ctx.fillStyle = color;
ctx.textBaseline = "top";
ctx.fillText(text, start_x, start_y);
return PNG.sync.read(canvas.toBuffer());
})

285
oled-i2c-rgpio/README.md Normal file
View File

@ -0,0 +1,285 @@
![npm version](http://img.shields.io/npm/v/oled-js.svg?style=flat) ![downloads over month](http://img.shields.io/npm/dm/oled-js.svg?style=flat)
OLED JS Pi over i2c-bus
========================
## What is this?
This is fork of package [`oled-js-pi`](https://github.com/kd7yva/oled-js-pi) that works thru `i2c-bus` package and not use package `i2c`.
A NodeJS driver for I2C/SPI compatible monochrome OLED screens; to be used on the Raspberry Pi! Works with 128 x 32, 128 x 64 and 96 x 16 sized screens, of the SSD1306 OLED/PLED Controller (read the [datasheet here](http://www.adafruit.com/datasheets/SSD1306.pdf)).
This based on the Blog Post and code by Suz Hinton - [Read her blog post about how OLED screens work](http://meow.noopkat.com/oled-js/)!
OLED screens are really cool - now you can control them with JavaScript!
## Install
If you haven't already, install [NodeJS](http://nodejs.org/).
`npm install oled-i2c-bus`
## I2C screens
Hook up I2C compatible oled to the Raspberry Pi. Pins: SDL and SCL
### I2C example
```javascript
var i2c = require('i2c-bus'),
i2cBus = i2c.openSync(1),
oled = require('oled-i2c-bus');
var opts = {
width: 128,
height: 64,
address: 0x3D
};
var oled = new oled(i2cBus, opts);
// do cool oled things here
```
### Wait, how do I find out the I2C address of my OLED screen?
Check your screen's documentation...
## Available methods
### clearDisplay
Fills the buffer with 'off' pixels (0x00). Optional bool argument specifies whether screen updates immediately with result. Default is true.
Usage:
```javascript
oled.clearDisplay();
```
### dimDisplay
Lowers the contrast on the display. This method takes one argument, a boolean. True for dimming, false to restore normal contrast.
Usage:
```javascript
oled.dimDisplay(true|false);
```
### invertDisplay
Inverts the pixels on the display. Black becomes white, white becomes black. This method takes one argument, a boolean. True for inverted state, false to restore normal pixel colors.
Usage:
```javascript
oled.invertDisplay(true|false);
```
### turnOffDisplay
Turns the display off.
Usage:
```javascript
oled.turnOffDisplay();
```
### turnOnDisplay
Turns the display on.
Usage:
```javascript
oled.turnOnDisplay();
```
### drawPixel
Draws a pixel at a specified position on the display. This method takes one argument: a multi-dimensional array containing either one or more sets of pixels.
Each pixel needs an x position, a y position, and a color. Colors can be specified as either 0 for 'off' or black, and 1 or 255 for 'on' or white.
Optional bool as last argument specifies whether screen updates immediately with result. Default is true.
Usage:
```javascript
// draws 4 white pixels total
// format: [x, y, color]
oled.drawPixel([
[128, 1, 1],
[128, 32, 1],
[128, 16, 1],
[64, 16, 1]
]);
```
### drawLine
Draws a one pixel wide line.
Arguments:
+ int **x0, y0** - start location of line
+ int **x1, y1** - end location of line
+ int **color** - can be specified as either 0 for 'off' or black, and 1 or 255 for 'on' or white.
Optional bool as last argument specifies whether screen updates immediately with result. Default is true.
Usage:
```javascript
// args: (x0, y0, x1, y1, color)
oled.drawLine(1, 1, 128, 32, 1);
```
### fillRect
Draws a filled rectangle.
Arguments:
+ int **x0, y0** - top left corner of rectangle
+ int **w, h** - width and height of rectangle
+ int **color** - can be specified as either 0 for 'off' or black, and 1 or 255 for 'on' or white.
Optional bool as last argument specifies whether screen updates immediately with result. Default is true.
Usage:
```javascript
// args: (x0, y0, x1, y1, color)
oled.fillRect(1, 1, 10, 20, 1);
```
### drawBitmap
Draws a bitmap using raw pixel data returned from an image parser. The image sourced must be monochrome, and indexed to only 2 colors. Resize the bitmap to your screen dimensions first. Using an image editor or ImageMagick might be required.
Optional bool as last argument specifies whether screen updates immediately with result. Default is true.
Tip: use a NodeJS image parser to get the pixel data, such as [pngparse](https://www.npmjs.org/package/pngparse). A demonstration of using this is below.
Example usage:
```
npm install pngparse
```
```javascript
var pngparse = require('pngparse');
pngparse.parseFile('indexed_file.png', function(err, image) {
oled.drawBitmap(image.data);
});
```
This method is provided as a primitive convenience. A better way to display images is to use NodeJS package [png-to-lcd](https://www.npmjs.org/package/png-to-lcd) instead. It's just as easy to use as drawBitmap, but is compatible with all image depths (lazy is good!). It will also auto-dither if you choose. You should still resize your image to your screen dimensions. This alternative method is covered below:
```
npm install png-to-lcd
```
```javascript
var pngtolcd = require('png-to-lcd');
pngtolcd('nyan-cat.png', true, function(err, bitmap) {
oled.buffer = bitmap;
oled.update();
});
```
### drawRGBAImage
Draw an RGBA coded image at specific coordinates. This only supports a monochrome
OLED so transparent pixels must be 100% transparent, off pixels should have an
RGB value of (0, 0, 0), and pixels with any color value will be considered on.
Use a library such as [pngjs](https://www.npmjs.com/package/pngjs) to read a png
file into the required rgba data structure.
Example:
```JavaScript
const fs = require('fs');
const PNG = require('pngjs').PNG;
const i2c = require('i2c-bus');
const oled = require('oled-i2c-bus');
var i2cBus = i2c.openSync(0);
var opts = {
width: 128,
height: 64,
address: 0x3C
};
var display = new oled(i2cBus, opts);
display.clearDisplay();
display.turnOnDisplay();
fs.createReadStream('./test.png')
.pipe(new PNG({ filterType: 4 }))
.on('parsed', function () {
setInterval(() => { drawImage(this) }, 1000);
});
function drawImage(image) {
let x = Math.floor(Math.random() * (display.WIDTH) - image.width / 2);
let y = Math.floor(Math.random() * (display.HEIGHT) - image.height / 2);
display.drawRGBAImage(image, x, y);
}
```
### startScroll
Scrolls the current display either left or right.
Arguments:
+ string **direction** - direction of scrolling. 'left' or 'right'
+ int **start** - starting row of scrolling area
+ int **stop** - end row of scrolling area
Usage:
```javascript
// args: (direction, start, stop)
oled.startscroll('left', 0, 15); // this will scroll an entire 128 x 32 screen
```
### stopScroll
Stops all current scrolling behaviour.
Usage:
```javascript
oled.stopscroll();
```
### setCursor
Sets the x and y position of 'cursor', when about to write text. This effectively helps tell the display where to start typing when writeString() method is called.
Call setCursor just before writeString().
Usage:
```javascript
// sets cursor to x = 1, y = 1
oled.setCursor(1, 1);
```
### writeString
Writes a string of text to the display.
Call setCursor() just before, if you need to set starting text position.
Arguments:
+ obj **font** - font object in JSON format (see note below on sourcing a font)
+ int **size** - font size, as multiplier. Eg. 2 would double size, 3 would triple etc.
+ string **text** - the actual text you want to show on the display.
+ int **color** - color of text. Can be specified as either 0 for 'off' or black, and 1 or 255 for 'on' or white.
+ bool **wrapping** - true applies word wrapping at the screen limit, false for no wrapping. If a long string without spaces is supplied as the text, just letter wrapping will apply instead.
Optional bool as last argument specifies whether screen updates immediately with result. Default is true.
Before all of this text can happen, you need to load a font buffer for use. A good font to start with is NodeJS package [oled-font-5x7](https://www.npmjs.org/package/oled-font-5x7).
Usage:
```
npm install oled-font-5x7
```
```javascript
var font = require('oled-font-5x7');
// sets cursor to x = 1, y = 1
oled.setCursor(1, 1);
oled.writeString(font, 1, 'Cats and dogs are really cool animals, you know.', 1, true);
```
### update
Sends the entire buffer in its current state to the oled display, effectively syncing the two. This method generally does not need to be called, unless you're messing around with the framebuffer manually before you're ready to sync with the display. It's also needed if you're choosing not to draw on the screen immediately with the built in methods.
Usage:
```javascript
oled.update();
```

View File

@ -0,0 +1,15 @@
OLED JS Pi over i2c-bus
========================
## What is this directory?
This directory contains a working example of NodeJS, a (typically) ARM board (Orange Pi Zero), and a 128x64 I2C SSD1306 display.
## Install
```
npm install
cp ../oled.js node_modules/oled-i2c-bus/
node clock.js
```

View File

@ -0,0 +1,25 @@
{
"name": "examples",
"version": "1.0.0",
"description": "Testing OLED SSD1306 display with nodeJS",
"main": "clock.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/haraldkubota/oled-i2c-bus"
},
"keywords": [
"oled",
"i2c"
],
"author": "Harald Kubota",
"license": "MIT",
"dependencies": {
"i2c-bus": "^1.1.2",
"oled-font-5x7": "^1.0.0",
"oled-i2c-bus": "^1.0.11",
"pngjs": "^3.3.3"
}
}

View File

@ -0,0 +1,39 @@
/*
* rgba.js
* Display an RGBA image at random locations on a small I2C connected display
*
* 2018-08-19 v1.0 Bryan Nielsen
*/
"use strict";
const fs = require('fs');
const PNG = require('pngjs').PNG;
const i2c = require('i2c-bus');
const oled = require('../oled');// 'oled-i2c-bus');
var i2cBus = i2c.openSync(0);
var opts = {
width: 128,
height: 64,
address: 0x3C
};
var display = new oled(i2cBus, opts);
display.clearDisplay();
display.turnOnDisplay();
fs.createReadStream('./test.png')
.pipe(new PNG({ filterType: 4 }))
.on('parsed', function () {
setInterval(() => { drawImage(this) }, 1000);
});
function drawImage(image) {
let x = Math.floor(Math.random() * (display.WIDTH) - image.width / 2);
let y = Math.floor(Math.random() * (display.HEIGHT) - image.height / 2);
display.drawRGBAImage(image, x, y);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

108
oled-i2c-rgpio/package-lock.json generated Normal file
View File

@ -0,0 +1,108 @@
{
"name": "oled-i2c-bus",
"version": "1.0.12",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "oled-i2c-bus",
"version": "1.0.12",
"dependencies": {
"rgpio": "file:local_modules/rgpio"
},
"devDependencies": {
"oled-font-5x7": "~1.0.0",
"png-to-lcd": "~1.0.2",
"pngjs": "^3.3.3",
"pngparse": "~2.0.1",
"temporal": "^0.3.8"
}
},
"local_modules/rgpio": {
"version": "0.0.1",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"bindings": "^1.5.0",
"node-addon-api": "^1.7.1"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/es6-shim": {
"version": "0.35.8",
"resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.8.tgz",
"integrity": "sha512-Twf7I2v4/1tLoIXMT8HlqaBSS5H2wQTs2wx3MNYCI8K1R1/clXyCazrcVCPm/FuO9cyV8+leEaZOWD5C253NDg==",
"dev": true
},
"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=="
},
"node_modules/floyd-steinberg": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/floyd-steinberg/-/floyd-steinberg-1.0.6.tgz",
"integrity": "sha512-gzlre+taSQzEY+nCusbHJFQ3zHXBkRAX4fe3szssPY/N/t1ClBX9hnHZM4o8ZvpdqLLUPEavuZ/Noj71ZaA8+A==",
"dev": true
},
"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=="
},
"node_modules/oled-font-5x7": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/oled-font-5x7/-/oled-font-5x7-1.0.3.tgz",
"integrity": "sha512-l25WvKft8CgXYxtaqKdYrAS1P91rnUUUIiOXojAOvjNCsfFzIl1aEsE2JuaRgMh1Euo7slm5lX0w+1qNkL8PpQ==",
"dev": true
},
"node_modules/png-to-lcd": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/png-to-lcd/-/png-to-lcd-1.0.3.tgz",
"integrity": "sha512-y4X4mRvZUoMv1ruQimuXixC72HfPyPZHCxlSiQkwVjBAdYlQSnkp1N3EZkgVoS+CngJdTGGW9nMw9VBkfSH39Q==",
"dev": true,
"dependencies": {
"floyd-steinberg": "~1.0.4",
"pngparse": "~2.0.1"
}
},
"node_modules/pngjs": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==",
"dev": true,
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pngparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pngparse/-/pngparse-2.0.1.tgz",
"integrity": "sha512-RyB1P0BBwt3CNIZ5wT53lR1dT3CUtopnMOuP8xZdHjPhI/uXNNRnkx1yQb/3MMMyyMeo6p19fiIRHcLopWIkxA==",
"dev": true
},
"node_modules/rgpio": {
"resolved": "local_modules/rgpio",
"link": true
},
"node_modules/temporal": {
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/temporal/-/temporal-0.3.8.tgz",
"integrity": "sha512-Oifg/Jy1FqoxgAHhfrwjnO9PKrqv9JYR/KgsmsMKjpPaYWdJmzWnCVhSFAxv7ygdYILj6Kd+v4YQtaxF0ZCjGA==",
"dev": true,
"dependencies": {
"es6-shim": "latest"
},
"engines": {
"node": ">=0.8.0"
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"name": "oled-i2c-bus",
"version": "1.0.12",
"description": "NodeJS module for controlling oled devices on the Raspbery Pi (including the SSD1306 OLED screens)",
"main": "oled.js",
"devDependencies": {
"oled-font-5x7": "~1.0.0",
"png-to-lcd": "~1.0.2",
"pngjs": "^3.3.3",
"pngparse": "~2.0.1",
"temporal": "^0.3.8"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"rgpio": "file:local_modules/rgpio"
}
}

23
oled_circle.js Normal file
View File

@ -0,0 +1,23 @@
const main = async () => {
var oled;
const _rg = require('rgpio');
const _oled = require('/home/ichij/ocogeclub/ocoge/blocks/sensors/ssd1306/oled.js');
await _rg.rgpio_sbc();
var _opts = {
width: 128,
height: 64,
address: 0x3c,
bus: 5
};
oled = new _oled(_rg, _opts);
oled.clearDisplay();
oled.turnOnDisplay();
oled.drawCircle(63, 31, 8, 1)
}
main();

1615
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,28 +22,37 @@
}, },
"homepage": "https://git.ocoge.club/ocoge.club/ocoge#readme", "homepage": "https://git.ocoge.club/ocoge.club/ocoge#readme",
"devDependencies": { "devDependencies": {
"@electron-forge/cli": "^6.2.1", "@electron-forge/cli": "^6.3.0",
"@electron-forge/maker-deb": "^6.2.1", "@electron-forge/maker-deb": "^6.3.0",
"@electron/rebuild": "^3.2.13", "@electron/rebuild": "^3.2.13",
"electron": "^25.2.0" "electron": "^26.0.0"
}, },
"dependencies": { "dependencies": {
"@blockly/field-slider": "^4.0.18", "@blockly/field-slider": "^6.0.3",
"@tensorflow-models/blazeface": "^0.0.7", "@napi-rs/canvas": "^0.1.43",
"@tensorflow-models/knn-classifier": "^1.2.4", "@ocoge/amg8833": "file:local_modules/amg8833",
"@tensorflow-models/mobilenet": "^2.1.0", "@ocoge/bme280": "file:local_modules/bme280",
"@ocoge/paj7620": "file:local_modules/paj7620",
"@ocoge/ssd1306": "file:local_modules/ssd1306",
"@tensorflow-models/blazeface": "^0.1.0",
"@tensorflow-models/knn-classifier": "^1.2.5",
"@tensorflow-models/mobilenet": "^2.1.1",
"@tensorflow-models/speech-commands": "^0.5.4", "@tensorflow-models/speech-commands": "^0.5.4",
"@tensorflow/tfjs-node": "^4.8.0", "@tensorflow/tfjs-node": "^4.10.0",
"amg8833": "file:local_modules/amg8833",
"axios": "^1.4.0", "axios": "^1.4.0",
"blockly": "^9.3.3", "blockly": "^10.1.2",
"canvas": "^2.11.2",
"dracula-prism": "^2.1.13", "dracula-prism": "^2.1.13",
"js-beautify": "^1.14.8", "js-beautify": "^1.14.9",
"node-abi": "^3.45.0", "node-abi": "^3.47.0",
"nodemailer": "^6.9.3", "nodemailer": "^6.9.4",
"oled-font-pack": "^1.0.1",
"pngjs": "^7.0.0",
"prismjs": "^1.29.0" "prismjs": "^1.29.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"rgpio": "file:local_modules/rgpio" "@ocoge/rgpio": "file:local_modules/rgpio"
}, },
"config": { "config": {
"forge": { "forge": {