ek15072809's picture
Update index.html
d586ae1 verified
<!-- Copyright (C) 2025 Yuna Hamaoka, Yuito Takasaki -->
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Arduinoビジュアルプログラミング</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f0f0f0;
}
.container {
display: flex;
gap: 20px;
max-width: 1200px;
margin: 0 auto;
align-items: stretch;
}
#block-area {
width: 60%;
height: 500px;
background-color: #fff;
border: 2px solid #ccc;
border-radius: 5px;
position: relative;
}
#code-area {
width: 40%;
height: 500px;
background-color: #333;
color: #fff;
border-radius: 5px;
padding: 10px;
overflow-y: auto;
font-family: monospace;
box-sizing: border-box;
position: relative;
}
#copy-button {
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
}
#copy-message {
position: absolute;
top: 40px;
right: 10px;
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
padding: 5px 10px;
border-radius: 3px;
font-size: 12px;
display: none;
}
.highlight {
background-color: #FFFF99 !important;
color: #000 !important;
}
.button-container {
max-width: 1200px;
margin: 20px auto 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.left-buttons {
display: flex;
gap: 10px;
}
.right-buttons {
display: flex;
gap: 10px;
}
button {
padding: 10px 20px;
font-size: 16px;
border: none;
border-radius: 5px;
cursor: pointer;
}
#save-btn {
background-color: #28a745;
color: white;
}
#save-btn:hover {
background-color: #218838;
}
#clear-btn {
background-color: #dc3545;
color: white;
}
#clear-btn:hover {
background-color: #b02a37;
}
#download-btn {
background-color: #007bff;
color: white;
}
#download-btn:hover {
background-color: #0056b3;
}
#print-btn {
background-color: #ffc107;
color: black;
}
#print-btn:hover {
background-color: #e0a800;
}
#exit-btn {
background-color: #6c757d;
color: white;
}
#exit-btn:hover {
background-color: #5a6268;
}
/* Blocklyズームコントロールのスタイル */
.blocklyZoom {
position: absolute;
bottom: 100px; /* ゴミ箱アイコンの上に配置 */
right: 43px;
display: flex;
flex-direction: column;
gap: 2px;
z-index: 10; /* ゴミ箱より手前に */
}
.blocklyZoom svg {
width: 32px;
height: 32px;
cursor: pointer;
}
/* ゴミ箱アイコンの位置調整 */
.blocklyTrash {
bottom: 10px !important;
right: 10px !important;
}
#serial-btn {
background-color: #17a2b8;
color: white;
}
#serial-btn:hover {
background-color: #138496;
}
</style>
</head>
<body>
<div class="container">
<div id="block-area"></div>
<div id="code-area">
<img id="copy-button" src="copy.png" alt="Copy Code" title="コードをコピー">
<div id="copy-message">クリップボードにコピーしました</div>
<pre id="generated-code">// 生成されたArduinoコードがここに表示されます</pre>
</div>
</div>
<div class="button-container">
<div class="left-buttons">
<button id="download-btn">ダウンロード</button>
<button id="serial-btn">シリアル通信</button> <!-- 追加 -->
<button id="clear-btn">クリア</button>
</div>
<div class="right-buttons">
</div>
</div>
<div id="serial-modal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000;">
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 875px; height: 400px; background: #fff; border-radius: 5px; overflow: hidden;">
<button id="close-modal" style="position: absolute; top: 10px; right: 10px; font-size: 20px; border: none; background: #fff; color: black; width: 30px; height: 30px; border-radius: 5px; cursor: pointer;">×</button>
<iframe id="serial-iframe" src="" style="width: 100%; height: calc(100% - 50px); margin-top: 50px; border: none;"></iframe>
</div>
</div>
<!-- ツールボックスの定義(カテゴリ付き) -->
<xml id="toolbox" style="display: none">
<category name="制御" colour="120">
<block type="setup"></block>
<block type="loop"></block>
<block type="delay">
<value name="TIME">
<shadow type="math_number">
<field name="NUM">1000</field>
</shadow>
</value>
</block>
<block type="delay_value">
<value name="TIME">
<shadow type="math_number">
<field name="NUM">1000</field>
</shadow>
</value>
</block>
<block type="delay_microseconds">
<value name="TIME">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
<block type="delay_microseconds_value">
<value name="TIME">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
<block type="controls_while"></block>
<block type="controls_if"></block>
<block type="repeat_times">
<value name="TIMES">
<shadow type="math_number">
<field name="NUM">5</field>
</shadow>
</value>
</block>
<block type="if_condition"></block>
<block type="wait_until_condition"></block>
</category>
<category name="ピン" colour="160">
<block type="pin_mode">
<value name="PIN">
<shadow type="math_number">
<field name="NUM">13</field>
</shadow>
</value>
</block>
<block type="digital_write">
<value name="PIN">
<shadow type="math_number">
<field name="NUM">13</field>
</shadow>
</value>
</block>
<block type="digital_read">
<value name="PIN">
<shadow type="math_number">
<field name="NUM">13</field>
</shadow>
</value>
</block>
<block type="analog_read"></block>
<block type="analog_write">
<value name="PIN">
<shadow type="math_number">
<field name="NUM">9</field>
</shadow>
</value>
<value name="VALUE">
<shadow type="math_number">
<field name="NUM">255</field>
</shadow>
</value>
</block>
</category>
<category name="テスト" colour="210">
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="string_compare">
<value name="A">
<shadow type="text">
<field name="TEXT">text1</field>
</shadow>
</value>
<value name="B">
<shadow type="text">
<field name="TEXT">text2</field>
</shadow>
</value>
</block>
</category>
<category name="計算" colour="230">
<block type="math_arithmetic"></block>
<block type="math_single"></block>
<block type="math_trig"></block>
<block type="random_number">
<value name="MIN">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
<value name="MAX">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
<block type="divide">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
<value name="DIV">
<shadow type="math_number">
<field name="NUM">2</field>
</shadow>
</value>
</block>
<block type="number_input">
<field name="VALUE">0</field>
</block>
<block type="string_input">
<field name="TEXT">Hello</field>
</block>
<block type="text_input">
<field name="TEXT">Hello</field>
</block>
</category>
<category name="変数" colour="260">
<block type="variable_declare_int"></block>
<block type="variable_set_int"></block>
<block type="variable_get_int"></block>
<block type="int_declare"></block>
<block type="int_set"></block>
<block type="int_get"></block>
<block type="float_declare"></block>
<block type="float_set"></block>
<block type="float_get"></block>
</category>
<category name="Generic Hardware" colour="290">
<block type="servo_write">
<value name="PIN">
<shadow type="math_number">
<field name="NUM">9</field>
</shadow>
</value>
<value name="ANGLE">
<shadow type="math_number">
<field name="NUM">90</field>
</shadow>
</value>
</block>
<block type="ultrasonic_read"></block>
<block type="ultrasonic_distance"></block>
<block type="lcd_print">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">Hello World!!</field>
</shadow>
</value>
</block>
<block type="lcd_print_value">
<value name="VALUE">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="lcd_clear"></block>
<block type="seven_segment_display">
<value name="NUMBER">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="tone">
<value name="FREQUENCY">
<shadow type="math_number">
<field name="NUM">440</field>
</shadow>
</value>
</block>
<block type="noTone"></block>
</category>
<category name="通信" colour="320">
<block type="serial_begin"></block>
<block type="serial_print">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">Hello</field>
</shadow>
</value>
</block>
<block type="serial_print_value">
<value name="VALUE">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="serial_println">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">Hello</field>
</shadow>
</value>
</block>
<block type="serial_println_value">
<value name="VALUE">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="serial_read_number"></block>
<block type="serial_read_string"></block>
</category>
<category name="拡張" colour="350">
<block type="custom_code"></block>
<block type="custom_string"></block>
<block type="custom_number"></block>
</category>
</xml>
<!-- BlocklyをCDNから読み込み -->
<script src="./blockly.min.js"></script>
<!-- 日本語対応 -->
<script src="./ja.js"></script>
<!-- pakoライブラリを読み込み(圧縮用) -->
<script src="./pako.min.js"></script>
<!-- html2canvasライブラリを読み込み(画像保存用) -->
<script src="html2canvas.min.js"></script>
<script>
// 文字列をクリーンアップする関数
function cleanString(str) {
if (!str) return '""';
let cleaned = str.replace(/^\s*\(?['"]?(.*?)['"]?\)?\s*$/, '$1');
if (cleaned === '') return '""';
if (!cleaned.startsWith('"') || !cleaned.endsWith('"')) {
cleaned = `"${cleaned.replace(/"/g, '\\"')}"`;
}
return cleaned;
}
// 7セグメントLEDの数字マッピング(カソードコモン、HIGH=点灯)
const segmentPatterns = [
[1, 1, 1, 1, 1, 1, 0], // 0
[0, 1, 1, 0, 0, 0, 0], // 1
[1, 1, 0, 1, 1, 0, 1], // 2
[1, 1, 1, 1, 0, 0, 1], // 3
[0, 1, 1, 0, 0, 1, 1], // 4
[1, 0, 1, 1, 0, 1, 1], // 5
[1, 0, 1, 1, 1, 1, 1], // 6
[1, 1, 1, 0, 0, 0, 0], // 7
[1, 1, 1, 1, 1, 1, 1], // 8
[1, 1, 1, 1, 0, 1, 1] // 9
];
// カスタムブロックの定義
// 制御
Blockly.Blocks['setup'] = {
init: function () {
this.appendStatementInput('SETUP_CODE').appendField('セットアップ');
this.setColour(120);
}
};
Blockly.Blocks['loop'] = {
init: function () {
this.appendStatementInput('LOOP_CODE').appendField('ループ');
this.setColour(120);
}
};
Blockly.Blocks['delay'] = {
init: function () {
this.appendDummyInput()
.appendField('待機:')
.appendField(new Blockly.FieldNumber(1000, 0), 'TIME')
.appendField('ms');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(120);
}
};
Blockly.Blocks['delay_value'] = {
init: function () {
this.appendValueInput('TIME')
.setCheck('Number')
.appendField('待機:');
this.appendDummyInput()
.appendField('ms');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(120);
}
};
Blockly.Blocks['delay_microseconds'] = {
init: function () {
this.appendDummyInput()
.appendField('待機:')
.appendField(new Blockly.FieldNumber(100, 0), 'TIME')
.appendField('μs');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(120);
}
};
Blockly.Blocks['delay_microseconds_value'] = {
init: function () {
this.appendValueInput('TIME')
.setCheck('Number')
.appendField('待機:');
this.appendDummyInput()
.appendField('μs');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(120);
}
};
Blockly.Blocks['controls_while'] = {
init: function () {
this.appendValueInput('CONDITION').appendField('条件が真の間繰り返す');
this.appendStatementInput('DO').appendField('実行');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(120);
}
};
Blockly.Blocks['controls_if'] = {
init: function () {
this.appendValueInput('IF0').appendField('もし');
this.appendStatementInput('DO0').appendField('なら');
this.appendStatementInput('ELSE').appendField('でなければ');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(120);
}
};
Blockly.Blocks['repeat_times'] = {
init: function () {
this.appendValueInput('TIMES')
.setCheck('Number')
.appendField('繰り返し:');
this.appendStatementInput('DO')
.appendField('回');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(120);
}
};
Blockly.Blocks['if_condition'] = {
init: function () {
this.appendValueInput('CONDITION')
.setCheck('Boolean')
.appendField('もし');
this.appendStatementInput('DO')
.appendField('なら');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(120);
}
};
Blockly.Blocks['wait_until_condition'] = {
init: function () {
this.appendValueInput('CONDITION')
.setCheck('Boolean')
.appendField('条件が真になるまで待つ');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(120);
}
};
// ピン
Blockly.Blocks['pin_mode'] = {
init: function () {
this.appendDummyInput()
.appendField('ピンのモードを設定:')
.appendField(new Blockly.FieldNumber(0, 0, 53), 'PIN')
.appendField(new Blockly.FieldDropdown([['出力', 'OUTPUT'], ['入力', 'INPUT'], ['入力プルアップ', 'INPUT_PULLUP']]), 'MODE');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(160);
}
};
Blockly.Blocks['digital_write'] = {
init: function () {
this.appendDummyInput()
.appendField('デジタル出力:')
.appendField(new Blockly.FieldNumber(0, 0, 53), 'PIN')
.appendField(new Blockly.FieldDropdown([['HIGH', 'HIGH'], ['LOW', 'LOW']]), 'VALUE');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(160);
}
};
Blockly.Blocks['digital_read'] = {
init: function () {
this.appendDummyInput()
.appendField('デジタル入力:')
.appendField(new Blockly.FieldNumber(0, 0, 53), 'PIN');
this.setOutput(true, 'Number');
this.setColour(160);
}
};
Blockly.Blocks['analog_read'] = {
init: function () {
this.appendDummyInput()
.appendField('アナログ入力:')
.appendField(new Blockly.FieldDropdown([
['A0', 'A0'], ['A1', 'A1'], ['A2', 'A2'], ['A3', 'A3'],
['A4', 'A4'], ['A5', 'A5'], ['A6', 'A6'], ['A7', 'A7'],
['A8', 'A8'], ['A9', 'A9'], ['A10', 'A10'], ['A11', 'A11'],
['A12', 'A12'], ['A13', 'A13'], ['A14', 'A14'], ['A15', 'A15']
]), 'PIN');
this.setOutput(true, 'Number');
this.setColour(160);
}
};
Blockly.Blocks['analog_write'] = {
init: function () {
this.appendDummyInput()
.appendField('アナログ出力:')
.appendField(new Blockly.FieldNumber(9, 0, 53), 'PIN');
this.appendValueInput('VALUE')
.setCheck('Number')
.appendField('値:');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(160);
}
};
// テスト
Blockly.Blocks['logic_compare'] = {
init: function () {
this.appendValueInput('A').setCheck('Number');
this.appendDummyInput()
.appendField(new Blockly.FieldDropdown([['=', 'EQ'], ['≠', 'NEQ'], ['<', 'LT'], ['≤', 'LTE'], ['>', 'GT'], ['≥', 'GTE']]), 'OP');
this.appendValueInput('B').setCheck('Number');
this.setOutput(true, 'Boolean');
this.setColour(210);
}
};
Blockly.Blocks['logic_operation'] = {
init: function () {
this.appendValueInput('A').setCheck('Boolean');
this.appendDummyInput()
.appendField(new Blockly.FieldDropdown([['かつ', 'AND'], ['または', 'OR']]), 'OP');
this.appendValueInput('B').setCheck('Boolean');
this.setOutput(true, 'Boolean');
this.setColour(210);
}
};
Blockly.Blocks['string_compare'] = {
init: function () {
this.appendValueInput('A').setCheck('String');
this.appendDummyInput()
.appendField(new Blockly.FieldDropdown([['=', 'EQ'], ['≠', 'NEQ']]), 'OP');
this.appendValueInput('B').setCheck('String');
this.setOutput(true, 'Boolean');
this.setColour(210);
}
};
// 計算
Blockly.Blocks['math_arithmetic'] = {
init: function () {
this.appendValueInput('A').setCheck('Number');
this.appendDummyInput()
.appendField(new Blockly.FieldDropdown([['+', 'ADD'], ['-', 'MINUS'], ['×', 'MULTIPLY'], ['÷', 'DIVIDE']]), 'OP');
this.appendValueInput('B').setCheck('Number');
this.setOutput(true, 'Number');
this.setColour(230);
}
};
Blockly.Blocks['math_single'] = {
init: function () {
this.appendValueInput('NUM').setCheck('Number')
.appendField(new Blockly.FieldDropdown([['絶対値', 'ABS'], ['平方根', 'SQRT']]), 'OP');
this.setOutput(true, 'Number');
this.setColour(230);
}
};
Blockly.Blocks['math_trig'] = {
init: function () {
this.appendValueInput('NUM').setCheck('Number')
.appendField(new Blockly.FieldDropdown([['sin', 'SIN'], ['cos', 'COS'], ['tan', 'TAN']]), 'OP');
this.setOutput(true, 'Number');
this.setColour(230);
}
};
Blockly.Blocks['random_number'] = {
init: function () {
this.appendValueInput('MIN')
.setCheck('Number')
.appendField('乱数:');
this.appendValueInput('MAX')
.setCheck('Number')
.appendField('から');
this.appendDummyInput()
.appendField('まで');
this.setOutput(true, 'Number');
this.setColour(230);
}
};
Blockly.Blocks['divide'] = {
init: function () {
this.appendValueInput('NUM')
.setCheck('Number')
.appendField('');
this.appendValueInput('DIV')
.setCheck('Number')
.appendField('を');
this.appendDummyInput()
.appendField('で割った値');
this.setOutput(true, 'Number');
this.setColour(230);
}
};
Blockly.Blocks['number_input'] = {
init: function () {
this.appendDummyInput()
.appendField('数値:')
.appendField(new Blockly.FieldNumber(0), 'VALUE');
this.setOutput(true, 'Number');
this.setColour(230);
}
};
Blockly.Blocks['string_input'] = {
init: function () {
this.appendDummyInput()
.appendField('文字列:')
.appendField(new Blockly.FieldTextInput('Hello'), 'TEXT');
this.setOutput(true, 'String');
this.setColour(230);
}
};
Blockly.Blocks['text_input'] = {
init: function () {
this.appendDummyInput()
.appendField('テキスト:')
.appendField(new Blockly.FieldTextInput('Hello'), 'TEXT');
this.setOutput(true, 'String');
this.setColour(230);
}
};
Blockly.Blocks['variable_set_number'] = {
init: function () {
this.appendDummyInput()
.appendField('数値変数')
.appendField(new Blockly.FieldVariable(null), 'VAR')
.appendField('に代入:');
this.appendValueInput('VALUE')
.setCheck('Number');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(230);
}
};
Blockly.Blocks['variable_set_string'] = {
init: function () {
this.appendDummyInput()
.appendField('文字列変数')
.appendField(new Blockly.FieldVariable(null), 'VAR')
.appendField('に代入:');
this.appendValueInput('TEXT')
.setCheck('String');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(230);
}
};
// 変数
Blockly.Blocks['variable_declare_int'] = {
init: function () {
this.appendDummyInput()
.appendField('整数変数宣言:')
.appendField(new Blockly.FieldVariable(null), 'VAR');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(260);
}
};
Blockly.Blocks['variable_set_int'] = {
init: function () {
this.appendValueInput('VALUE')
.setCheck('Number')
.appendField('整数変数')
.appendField(new Blockly.FieldVariable(null), 'VAR')
.appendField('に代入:');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(260);
}
};
Blockly.Blocks['variable_get_int'] = {
init: function () {
this.appendDummyInput()
.appendField('整数変数')
.appendField(new Blockly.FieldVariable(null), 'VAR');
this.setOutput(true, 'Number');
this.setColour(260);
}
};
Blockly.Blocks['int_declare'] = {
init: function () {
this.appendDummyInput()
.appendField('int変数宣言:')
.appendField(new Blockly.FieldVariable(null), 'VAR');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(260);
}
};
Blockly.Blocks['int_set'] = {
init: function () {
this.appendValueInput('VALUE')
.setCheck('Number')
.appendField('int変数')
.appendField(new Blockly.FieldVariable(null), 'VAR')
.appendField('に代入:');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(260);
}
};
Blockly.Blocks['int_get'] = {
init: function () {
this.appendDummyInput()
.appendField('int変数')
.appendField(new Blockly.FieldVariable(null), 'VAR');
this.setOutput(true, 'Number');
this.setColour(260);
}
};
Blockly.Blocks['float_declare'] = {
init: function () {
this.appendDummyInput()
.appendField('float変数宣言:')
.appendField(new Blockly.FieldVariable(null), 'VAR');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(260);
}
};
Blockly.Blocks['float_set'] = {
init: function () {
this.appendValueInput('VALUE')
.setCheck('Number')
.appendField('float変数')
.appendField(new Blockly.FieldVariable(null), 'VAR')
.appendField('に代入:');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(260);
}
};
Blockly.Blocks['float_get'] = {
init: function () {
this.appendDummyInput()
.appendField('float変数')
.appendField(new Blockly.FieldVariable(null), 'VAR');
this.setOutput(true, 'Number');
this.setColour(260);
}
};
// Generic Hardware
Blockly.Blocks['servo_write'] = {
init: function () {
this.appendDummyInput()
.appendField('サーボ:')
.appendField(new Blockly.FieldNumber(9, 0, 53), 'PIN');
this.appendValueInput('ANGLE')
.setCheck('Number')
.appendField('角度:');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(290);
}
};
Blockly.Blocks['ultrasonic_read'] = {
init: function () {
this.appendDummyInput()
.appendField('超音波センサ: トリガ')
.appendField(new Blockly.FieldNumber(12, 0, 53), 'TRIG')
.appendField('エコー')
.appendField(new Blockly.FieldNumber(11, 0, 53), 'ECHO');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(290);
}
};
Blockly.Blocks['ultrasonic_distance'] = {
init: function () {
this.appendDummyInput()
.appendField('超音波センサ')
.appendField('トリガ')
.appendField(new Blockly.FieldNumber(12, 0, 53), 'TRIG');
this.setOutput(true, 'Number');
this.setColour(290);
}
};
Blockly.Blocks['lcd_print'] = {
init: function () {
this.appendDummyInput()
.appendField('LCD表示 ピン RS:')
.appendField(new Blockly.FieldNumber(8, 0, 53), 'RS')
.appendField('E:')
.appendField(new Blockly.FieldNumber(9, 0, 53), 'E')
.appendField('D4:')
.appendField(new Blockly.FieldNumber(4, 0, 53), 'D4');
this.appendDummyInput()
.appendField('D5:')
.appendField(new Blockly.FieldNumber(5, 0, 53), 'D5')
.appendField('D6:')
.appendField(new Blockly.FieldNumber(6, 0, 53), 'D6')
.appendField('D7:')
.appendField(new Blockly.FieldNumber(7, 0, 53), 'D7');
this.appendDummyInput()
.appendField('列数:')
.appendField(new Blockly.FieldNumber(16, 1), 'COLS')
.appendField('行数:')
.appendField(new Blockly.FieldNumber(2, 1), 'ROWS');
this.appendDummyInput()
.appendField('カーソル 列:')
.appendField(new Blockly.FieldNumber(0, 0), 'CURSOR_COL')
.appendField('行:')
.appendField(new Blockly.FieldNumber(0, 0), 'CURSOR_ROW');
this.appendValueInput('TEXT')
.appendField('テキスト:');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(290);
}
};
Blockly.Blocks['lcd_print_value'] = {
init: function () {
this.appendDummyInput()
.appendField('LCD数値表示 ピン RS:')
.appendField(new Blockly.FieldNumber(8, 0, 53), 'RS')
.appendField('E:')
.appendField(new Blockly.FieldNumber(9, 0, 53), 'E')
.appendField('D4:')
.appendField(new Blockly.FieldNumber(4, 0, 53), 'D4');
this.appendDummyInput()
.appendField('D5:')
.appendField(new Blockly.FieldNumber(5, 0, 53), 'D5')
.appendField('D6:')
.appendField(new Blockly.FieldNumber(6, 0, 53), 'D6')
.appendField('D7:')
.appendField(new Blockly.FieldNumber(7, 0, 53), 'D7');
this.appendDummyInput()
.appendField('列数:')
.appendField(new Blockly.FieldNumber(16, 1), 'COLS')
.appendField('行数:')
.appendField(new Blockly.FieldNumber(2, 1), 'ROWS');
this.appendDummyInput()
.appendField('カーソル 列:')
.appendField(new Blockly.FieldNumber(0, 0), 'CURSOR_COL')
.appendField('行:')
.appendField(new Blockly.FieldNumber(0, 0), 'CURSOR_ROW');
this.appendValueInput('VALUE')
.setCheck('Number')
.appendField('数値:');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(290);
}
};
Blockly.Blocks['lcd_clear'] = {
init: function () {
this.appendDummyInput()
.appendField('LCDクリア');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(290);
}
};
Blockly.Blocks['seven_segment_display'] = {
init: function () {
this.appendDummyInput()
.appendField('7セグメントLED表示 ピン a:')
.appendField(new Blockly.FieldNumber(2, 0, 53), 'PIN_A')
.appendField('b:')
.appendField(new Blockly.FieldNumber(3, 0, 53), 'PIN_B')
.appendField('c:')
.appendField(new Blockly.FieldNumber(4, 0, 53), 'PIN_C');
this.appendDummyInput()
.appendField('d:')
.appendField(new Blockly.FieldNumber(5, 0, 53), 'PIN_D')
.appendField('e:')
.appendField(new Blockly.FieldNumber(6, 0, 53), 'PIN_E')
.appendField('f:')
.appendField(new Blockly.FieldNumber(7, 0, 53), 'PIN_F');
this.appendDummyInput()
.appendField('g:')
.appendField(new Blockly.FieldNumber(8, 0, 53), 'PIN_G');
this.appendValueInput('NUMBER')
.setCheck('Number')
.appendField('数字:');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(290);
}
};
Blockly.Blocks['tone'] = {
init: function () {
this.appendDummyInput()
.appendField('トーン出力 ピン:')
.appendField(new Blockly.FieldNumber(9, 0, 53), 'PIN');
this.appendValueInput('FREQUENCY')
.setCheck('Number')
.appendField('周波数(Hz):');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(290);
}
};
Blockly.Blocks['noTone'] = {
init: function () {
this.appendDummyInput()
.appendField('トーン停止 ピン:')
.appendField(new Blockly.FieldNumber(9, 0, 53), 'PIN');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(290);
}
};
// 通信
Blockly.Blocks['serial_begin'] = {
init: function () {
this.appendDummyInput()
.appendField('シリアル開始:')
.appendField(new Blockly.FieldNumber(9600), 'BAUD');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(320);
}
};
Blockly.Blocks['serial_print'] = {
init: function () {
this.appendValueInput('TEXT')
.appendField('シリアル出力(改行なし):');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(320);
}
};
Blockly.Blocks['serial_print_value'] = {
init: function () {
this.appendValueInput('VALUE')
.setCheck('Number')
.appendField('シリアル出力(数値、改行なし):');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(320);
}
};
Blockly.Blocks['serial_println'] = {
init: function () {
this.appendValueInput('TEXT')
.appendField('シリアル出力:');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(320);
}
};
Blockly.Blocks['serial_println_value'] = {
init: function () {
this.appendValueInput('VALUE')
.setCheck('Number')
.appendField('シリアル出力(数値):');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(320);
}
};
Blockly.Blocks['serial_read_number'] = {
init: function () {
this.appendDummyInput()
.appendField('シリアル入力(数値)');
this.setOutput(true, 'Number');
this.setColour(320);
}
};
Blockly.Blocks['serial_read_string'] = {
init: function () {
this.appendDummyInput()
.appendField('シリアル入力(文字列)');
this.setOutput(true, 'String');
this.setColour(320);
}
};
// 拡張
Blockly.Blocks['custom_code'] = {
init: function () {
this.appendDummyInput()
.appendField('カスタムコード:')
.appendField(new Blockly.FieldTextInput(''), 'CODE');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setColour(350);
}
};
Blockly.Blocks['custom_string'] = {
init: function () {
this.appendDummyInput()
.appendField('カスタム文字列:')
.appendField(new Blockly.FieldTextInput(''), 'TEXT');
this.setOutput(true, 'String');
this.setColour(350);
}
};
Blockly.Blocks['custom_number'] = {
init: function () {
this.appendDummyInput()
.appendField('カスタム数値:')
.appendField(new Blockly.FieldTextInput(''), 'NUMBER');
this.setOutput(true, 'Number');
this.setColour(350);
}
};
// Blocklyワークスペースを初期化
const workspace = Blockly.inject('block-area', {
toolbox: document.getElementById('toolbox'),
scrollbars: true,
trashcan: true,
sounds: true,
zoom: {
controls: false, // デフォルトのズームコントロールを無効化
wheel: false,
startScale: 1.0,
maxScale: 2.0,
minScale: 0.5,
scaleSpeed: 1.2
}
});
// ズームコントロールを手動で追加(インラインSVGを使用)
const zoomControls = document.createElement('div');
zoomControls.className = 'blocklyZoom';
zoomControls.innerHTML = `
<svg id="zoom-in" viewBox="0 0 32 32" title="拡大">
<circle cx="16" cy="16" r="10" fill="none" stroke="#000" stroke-width="2"/>
<path d="M12 16 h8 M16 12 v8" stroke="#000" stroke-width="2"/>
<line x1="22" y1="22" x2="28" y2="28" stroke="#000" stroke-width="2"/>
</svg>
<svg id="zoom-out" viewBox="0 0 32 32" title="縮小">
<circle cx="16" cy="16" r="10" fill="none" stroke="#000" stroke-width="2"/>
<path d="M12 16 h8" stroke="#000" stroke-width="2"/>
<line x1="22" y1="22" x2="28" y2="28" stroke="#000" stroke-width="2"/>
</svg>
<svg id="zoom-reset" viewBox="0 0 32 32" title="中央寄せ">
<path d="M8 16 h16 M16 8 v16" stroke="#000" stroke-width="2"/>
<path d="M8 12 l-4 4 l4 4 M24 12 l4 4 l-4 4" fill="none" stroke="#000" stroke-width="2"/>
</svg>
`;
document.getElementById('block-area').appendChild(zoomControls);
// ズームと中央寄せのイベントリスナー
document.getElementById('zoom-in').addEventListener('click', function () {
workspace.zoomCenter(1); // スケールを増加(ズームイン)
});
document.getElementById('zoom-out').addEventListener('click', function () {
workspace.zoomCenter(-1); // スケールを減少(ズームアウト)
});
document.getElementById('zoom-reset').addEventListener('click', function () {
const topBlocks = workspace.getTopBlocks(true);
if (topBlocks.length > 0) {
workspace.centerOnBlock(topBlocks[0].id);
} else {
workspace.scrollCenter();
}
});
// コピーボタンのイベントリスナー
document.getElementById('copy-button').addEventListener('click', function () {
const code = document.getElementById('generated-code').textContent;
navigator.clipboard.writeText(code).then(() => {
const copyMessage = document.getElementById('copy-message');
copyMessage.style.display = 'block';
setTimeout(() => {
copyMessage.style.display = 'none';
}, 3000);
}).catch(err => {
console.error('コードのコピーに失敗しました:', err);
});
});
// URLからワークスペースを復元
function loadWorkspaceFromUrl() {
const urlParams = new URLSearchParams(window.location.search);
const compressed = urlParams.get('blocks');
if (compressed) {
try {
const binaryString = atob(compressed);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
const xmlString = pako.inflate(bytes, { to: 'string' });
const xml = Blockly.utils.xml.textToDom(xmlString);
workspace.clear();
Blockly.Xml.domToWorkspace(xml, workspace);
} catch (e) {
console.error('ワークスペースの復元に失敗しました:', e);
}
}
}
// ワークスペースをURLに保存
function saveWorkspaceToUrl() {
try {
const xml = Blockly.Xml.workspaceToDom(workspace);
const xmlString = Blockly.Xml.domToText(xml);
const compressed = pako.gzip(xmlString, { to: 'string' });
const base64 = btoa(String.fromCharCode.apply(null, compressed));
const url = new URL(window.location);
url.searchParams.set('blocks', base64);
history.pushState({}, '', url);
} catch (e) {
console.error('ワークスペースの保存に失敗しました:', e);
}
}
// Arduinoコード生成ロジック
function generateArduinoCode() {
let includes = new Set();
let variables = new Set();
let servoObjects = new Set();
let servoSetup = new Set();
let lcdObjects = new Set();
let lcdSetup = new Set();
let sevenSegmentSetup = new Set();
let code = '';
const topBlocks = workspace.getTopBlocks(true);
let lcdPinConfig = null;
function collectDependencies(block) {
if (!block) return;
let currentBlock = block;
while (currentBlock) {
if (currentBlock.type === 'variable_declare_int' || currentBlock.type === 'variable_set_int' || currentBlock.type === 'variable_get_int') {
const varField = currentBlock.getField('VAR');
if (varField && workspace.getVariableMap()) {
const variable = varField.getVariable();
if (variable) {
const varName = variable.name;
if (varName) {
variables.add(`uint8_t ${varName};`);
}
}
}
} else if (currentBlock.type === 'int_declare' || currentBlock.type === 'int_set' || currentBlock.type === 'int_get') {
const varField = currentBlock.getField('VAR');
if (varField && workspace.getVariableMap()) {
const variable = varField.getVariable();
if (variable) {
const varName = variable.name;
if (varName) {
variables.add(`int ${varName};`);
}
}
}
} else if (currentBlock.type === 'float_declare' || currentBlock.type === 'float_set' || currentBlock.type === 'float_get') {
const varField = currentBlock.getField('VAR');
if (varField && workspace.getVariableMap()) {
const variable = varField.getVariable();
if (variable) {
const varName = variable.name;
if (varName) {
variables.add(`float ${varName};`);
}
}
}
} else if (currentBlock.type === 'variable_set_number' || currentBlock.type === 'variable_set_string') {
const varField = currentBlock.getField('VAR');
if (varField && workspace.getVariableMap()) {
const variable = varField.getVariable();
if (variable) {
const varName = variable.name;
if (varName) {
if (currentBlock.type === 'variable_set_number') {
variables.add(`float ${varName};`);
} else if (currentBlock.type === 'variable_set_string') {
variables.add(`String ${varName};`);
}
}
}
}
} else if (currentBlock.type === 'servo_write') {
const pinServo = currentBlock.getFieldValue('PIN');
includes.add('#include <Servo.h>');
servoObjects.add(`Servo servo_${pinServo};`);
servoSetup.add(`servo_${pinServo}.attach(${pinServo});`);
} else if (currentBlock.type === 'lcd_print' || currentBlock.type === 'lcd_print_value') {
includes.add('#include <LiquidCrystal.h>');
const rs = currentBlock.getFieldValue('RS');
const e = currentBlock.getFieldValue('E');
const d4 = currentBlock.getFieldValue('D4');
const d5 = currentBlock.getFieldValue('D5');
const d6 = currentBlock.getFieldValue('D6');
const d7 = currentBlock.getFieldValue('D7');
const cols = currentBlock.getFieldValue('COLS');
const rows = currentBlock.getFieldValue('ROWS');
const newPinConfig = `${rs},${e},${d4},${d5},${d6},${d7}`;
if (!lcdPinConfig) {
lcdPinConfig = newPinConfig;
lcdObjects.add(`LiquidCrystal lcd(${rs}, ${e}, ${d4}, ${d5}, ${d6}, ${d7});`);
lcdSetup.add(`lcd.begin(${cols}, ${rows});`);
lcdSetup.add(`lcd.clear();`);
} else if (lcdPinConfig !== newPinConfig) {
console.warn(`LCDピンの設定が異なります。最初の設定(${lcdPinConfig})を使用します。`);
}
} else if (currentBlock.type === 'lcd_clear') {
includes.add('#include <LiquidCrystal.h>');
if (!lcdPinConfig) {
lcdPinConfig = '8,9,4,5,6,7';
lcdObjects.add(`LiquidCrystal lcd(8, 9, 4, 5, 6, 7);`);
lcdSetup.add(`lcd.begin(16, 2);`);
lcdSetup.add(`lcd.clear();`);
}
} else if (currentBlock.type === 'seven_segment_display') {
const pinA = currentBlock.getFieldValue('PIN_A');
const pinB = currentBlock.getFieldValue('PIN_B');
const pinC = currentBlock.getFieldValue('PIN_C');
const pinD = currentBlock.getFieldValue('PIN_D');
const pinE = currentBlock.getFieldValue('PIN_E');
const pinF = currentBlock.getFieldValue('PIN_F');
const pinG = currentBlock.getFieldValue('PIN_G');
sevenSegmentSetup.add(`pinMode(${pinA}, OUTPUT);`);
sevenSegmentSetup.add(`pinMode(${pinB}, OUTPUT);`);
sevenSegmentSetup.add(`pinMode(${pinC}, OUTPUT);`);
sevenSegmentSetup.add(`pinMode(${pinD}, OUTPUT);`);
sevenSegmentSetup.add(`pinMode(${pinE}, OUTPUT);`);
sevenSegmentSetup.add(`pinMode(${pinF}, OUTPUT);`);
sevenSegmentSetup.add(`pinMode(${pinG}, OUTPUT);`);
} else if (currentBlock.type === 'ultrasonic_read') {
const trig = currentBlock.getFieldValue('TRIG');
const echo = currentBlock.getFieldValue('ECHO');
variables.add(`long duration_${trig};`);
variables.add(`int distance_${trig};`);
sevenSegmentSetup.add(`pinMode(${trig}, OUTPUT);`);
sevenSegmentSetup.add(`pinMode(${echo}, INPUT);`);
} else if (currentBlock.type === 'tone') {
const pin = currentBlock.getFieldValue('PIN');
sevenSegmentSetup.add(`pinMode(${pin}, OUTPUT);`);
}
if (currentBlock.getInput('DO')) collectDependencies(currentBlock.getInputTargetBlock('DO'));
if (currentBlock.getInput('DO0')) collectDependencies(currentBlock.getInputTargetBlock('DO0'));
if (currentBlock.getInput('ELSE')) collectDependencies(currentBlock.getInputTargetBlock('ELSE'));
if (currentBlock.getInput('SETUP_CODE')) collectDependencies(currentBlock.getInputTargetBlock('SETUP_CODE'));
if (currentBlock.getInput('LOOP_CODE')) collectDependencies(currentBlock.getInputTargetBlock('LOOP_CODE'));
if (currentBlock.getInput('VALUE')) collectDependencies(currentBlock.getInputTargetBlock('VALUE'));
if (currentBlock.getInput('TEXT')) collectDependencies(currentBlock.getInputTargetBlock('TEXT'));
if (currentBlock.getInput('NUMBER')) collectDependencies(currentBlock.getInputTargetBlock('NUMBER'));
if (currentBlock.getInput('FREQUENCY')) collectDependencies(currentBlock.getInputTargetBlock('FREQUENCY'));
if (currentBlock.getInput('ANGLE')) collectDependencies(currentBlock.getInputTargetBlock('ANGLE'));
if (currentBlock.getInput('TIME')) collectDependencies(currentBlock.getInputTargetBlock('TIME'));
if (currentBlock.getInput('A')) collectDependencies(currentBlock.getInputTargetBlock('A'));
if (currentBlock.getInput('B')) collectDependencies(currentBlock.getInputTargetBlock('B'));
currentBlock = currentBlock.getNextBlock();
}
}
topBlocks.forEach(block => collectDependencies(block));
if (includes.size > 0) {
code += Array.from(includes).join('\n') + '\n\n';
}
if (variables.size > 0 || servoObjects.size > 0 || lcdObjects.size > 0) {
code += [...variables, ...servoObjects, ...lcdObjects].join('\n') + '\n\n';
}
topBlocks.forEach(block => {
if (block.type === 'setup') {
code += 'void setup() {\n';
if (servoSetup.size > 0 || lcdSetup.size > 0 || sevenSegmentSetup.size > 0) {
code += ` ${[...servoSetup, ...lcdSetup, ...sevenSegmentSetup].join('\n ')}\n`;
}
const setupCode = generateCodeForBlock(block.getInputTargetBlock('SETUP_CODE'));
code += setupCode ? ` ${setupCode.replace(/\n/g, '\n ')}` : '';
code += '\n}\n\n';
} else if (block.type === 'loop') {
code += 'void loop() {\n';
const loopCode = generateCodeForBlock(block.getInputTargetBlock('LOOP_CODE'));
code += loopCode ? ` ${loopCode.replace(/\n/g, '\n ')}` : '';
code += '\n}\n';
}
});
return code || '// コードが生成されていません';
}
// Arduinoコード生成ロジック内のgenerateValueCode関数
function generateValueCode(block) {
if (!block) return '';
switch (block.type) {
case 'digital_read':
const pinDr = block.getFieldValue('PIN');
return `digitalRead(${pinDr})`;
case 'analog_read':
const pinAr = block.getFieldValue('PIN');
return `analogRead(${pinAr})`;
case 'serial_read_number':
return `Serial.parseInt()`;
case 'serial_read_string':
return `Serial.readString()`; // クリーンアップせず、そのまま返す
case 'logic_compare':
const aComp = generateValueCode(block.getInputTargetBlock('A')) || '0';
const opComp = block.getFieldValue('OP');
const bComp = generateValueCode(block.getInputTargetBlock('B')) || '0';
const operators = { 'EQ': '==', 'NEQ': '!=', 'LT': '<', 'LTE': '<=', 'GT': '>', 'GTE': '>=' };
return `(${aComp} ${operators[opComp]} ${bComp})`;
case 'string_compare':
let aStr = generateValueCode(block.getInputTargetBlock('A')) || '""';
let bStr = generateValueCode(block.getInputTargetBlock('B')) || '""';
const opStr = block.getFieldValue('OP');
// Serial.readString()の場合、クリーンアップしない
if (block.getInputTargetBlock('A')?.type !== 'serial_read_string') {
aStr = cleanString(aStr);
}
if (block.getInputTargetBlock('B')?.type !== 'serial_read_string') {
bStr = cleanString(bStr);
}
if (opStr === 'EQ') {
return `(${aStr} == ${bStr})`;
} else { // NEQ
return `(${aStr} != ${bStr})`;
}
case 'logic_operation':
const aLogic = generateValueCode(block.getInputTargetBlock('A')) || 'true';
const opLogic = block.getFieldValue('OP');
const bLogic = generateValueCode(block.getInputTargetBlock('B')) || 'true';
const logicOps = { 'AND': '&&', 'OR': '||' };
return `(${aLogic} ${logicOps[opLogic]} ${bLogic})`;
case 'math_arithmetic':
const aMath = generateValueCode(block.getInputTargetBlock('A')) || '0';
const opMath = block.getFieldValue('OP');
const bMath = generateValueCode(block.getInputTargetBlock('B')) || '0';
const mathOps = { 'ADD': '+', 'MINUS': '-', 'MULTIPLY': '*', 'DIVIDE': '/' };
return `(${aMath} ${mathOps[opMath]} ${bMath})`;
case 'math_single':
const numSingle = generateValueCode(block.getInputTargetBlock('NUM')) || '0';
const opSingle = block.getFieldValue('OP');
return `${opSingle.toLowerCase()}(${numSingle})`;
case 'math_trig':
const numTrig = generateValueCode(block.getInputTargetBlock('NUM')) || '0';
const opTrig = block.getFieldValue('OP');
return `${opTrig.toLowerCase()}(${numTrig})`;
case 'random_number':
const min = generateValueCode(block.getInputTargetBlock('MIN')) || '0';
const max = generateValueCode(block.getInputTargetBlock('MAX')) || '100';
return `random(${min}, ${max})`;
case 'divide':
const num = generateValueCode(block.getInputTargetBlock('NUM')) || '10';
const div = generateValueCode(block.getInputTargetBlock('DIV')) || '2';
return `(${num} / ${div})`;
case 'number_input':
const numberValue = block.getFieldValue('VALUE');
return `${numberValue}`;
case 'string_input':
const stringValue = block.getFieldValue('TEXT');
return `"${stringValue.replace(/"/g, '\\"')}"`; // 通常の文字列はクリーンアップ
case 'text_input':
const textValue = block.getFieldValue('TEXT');
return `"${textValue.replace(/"/g, '\\"')}"`; // 通常の文字列はクリーンアップ
case 'custom_string':
const customStringValue = block.getFieldValue('TEXT');
return `"${customStringValue.replace(/"/g, '\\"')}"`;
case 'custom_number':
const customNumberValue = block.getFieldValue('NUMBER');
return `${customNumberValue}`; // そのまま数値として扱う
case 'variable_get_int':
const varField = block.getField('VAR');
return varField && varField.getVariable() ? varField.getVariable().name : 'unknown_var';
case 'int_get':
const varFieldIntGet = block.getField('VAR');
return varFieldIntGet && varFieldIntGet.getVariable() ? varFieldIntGet.getVariable().name : 'unknown_var';
case 'float_get':
const varFieldFloatGet = block.getField('VAR');
return varFieldFloatGet && varFieldFloatGet.getVariable() ? varFieldFloatGet.getVariable().name : 'unknown_var';
case 'ultrasonic_distance':
const trig = block.getFieldValue('TRIG');
return `distance_${trig}`;
default:
return '';
}
}
function generateCodeForBlock(block) {
if (!block) return '';
let code = '';
while (block) {
switch (block.type) {
case 'pin_mode':
const pin = block.getFieldValue('PIN');
const mode = block.getFieldValue('MODE');
code += `pinMode(${pin}, ${mode});\n`;
break;
case 'digital_write':
const pinDw = block.getFieldValue('PIN');
const value = block.getFieldValue('VALUE');
code += `digitalWrite(${pinDw}, ${value});\n`;
break;
case 'analog_write':
const pinAw = block.getFieldValue('PIN');
const valueAw = generateValueCode(block.getInputTargetBlock('VALUE')) || '255';
code += `analogWrite(${pinAw}, ${valueAw});\n`;
break;
case 'delay':
const time = block.getFieldValue('TIME');
code += `delay(${time});\n`;
break;
case 'delay_microseconds':
const timeUs = block.getFieldValue('TIME');
code += `delayMicroseconds(${timeUs});\n`;
break;
case 'delay_microseconds_value':
const timeUsValue = generateValueCode(block.getInputTargetBlock('TIME')) || '100';
code += `delayMicroseconds(${timeUsValue});\n`;
break;
case 'delay_value':
const timeValue = generateValueCode(block.getInputTargetBlock('TIME')) || '1000';
code += `delay(${timeValue});\n`;
break;
case 'controls_while':
const conditionW = generateValueCode(block.getInputTargetBlock('CONDITION')) || 'true';
const doCodeW = generateCodeForBlock(block.getInputTargetBlock('DO'));
code += `while (${conditionW}) {\n ${doCodeW.replace(/\n/g, '\n ')}\n}\n`;
break;
case 'controls_if':
const conditionIf = generateValueCode(block.getInputTargetBlock('IF0')) || 'true';
const doCodeIf = generateCodeForBlock(block.getInputTargetBlock('DO0'));
const elseCode = generateCodeForBlock(block.getInputTargetBlock('ELSE'));
code += `if (${conditionIf}) {\n ${doCodeIf.replace(/\n/g, '\n ')}\n}`;
if (elseCode) code += ` else {\n ${elseCode.replace(/\n/g, '\n ')}\n}`;
code += '\n';
break;
case 'repeat_times':
const times = generateValueCode(block.getInputTargetBlock('TIMES')) || '5';
const doCode = generateCodeForBlock(block.getInputTargetBlock('DO'));
code += `for (int i = 0; i < ${times}; i++) {\n ${doCode.replace(/\n/g, '\n ')}\n}\n`;
break;
case 'if_condition':
const condition = generateValueCode(block.getInputTargetBlock('CONDITION')) || 'true';
const ifCode = generateCodeForBlock(block.getInputTargetBlock('DO'));
code += `if (${condition}) {\n ${ifCode.replace(/\n/g, '\n ')}\n}\n`;
break;
case 'wait_until_condition':
const waitCondition = generateValueCode(block.getInputTargetBlock('CONDITION')) || 'false';
code += `while (!${waitCondition}) {\n delay(10);\n}\n`;
break;
case 'variable_declare_int':
break;
case 'variable_set_int':
const varField = block.getField('VAR');
const varName = varField && varField.getVariable() ? varField.getVariable().name : 'unknown_var';
const valueIntNew = generateValueCode(block.getInputTargetBlock('VALUE')) || '0';
code += `${varName} = ${valueIntNew};\n`;
break;
case 'int_declare':
break;
case 'int_set':
const varFieldInt = block.getField('VAR');
const varNameInt = varFieldInt && varFieldInt.getVariable() ? varFieldInt.getVariable().name : 'unknown_var';
const valueInt = generateValueCode(block.getInputTargetBlock('VALUE')) || '0';
code += `${varNameInt} = ${valueInt};\n`;
break;
case 'float_declare':
break;
case 'float_set':
const varFieldFloat = block.getField('VAR');
const varNameFloat = varFieldFloat && varFieldFloat.getVariable() ? varFieldFloat.getVariable().name : 'unknown_var';
const valueFloat = generateValueCode(block.getInputTargetBlock('VALUE')) || '0.0';
code += `${varNameFloat} = ${valueFloat};\n`;
break;
case 'variable_set_number':
const varFieldNum = block.getField('VAR');
const varNameNum = varFieldNum && varFieldNum.getVariable() ? varFieldNum.getVariable().name : 'unknown_var';
const valueNum = generateValueCode(block.getInputTargetBlock('VALUE')) || '0';
code += `${varNameNum} = ${valueNum};\n`;
break;
case 'variable_set_string':
const varFieldStr = block.getField('VAR');
const varNameStr = varFieldStr && varFieldStr.getVariable() ? varFieldStr.getVariable().name : 'unknown_var';
let valueStr = generateValueCode(block.getInputTargetBlock('TEXT')) || '""';
if (block.getInputTargetBlock('TEXT')?.type !== 'serial_read_string') {
valueStr = cleanString(valueStr);
}
code += `${varNameStr} = ${valueStr};\n`;
break;
case 'servo_write':
const pinServo = block.getFieldValue('PIN');
const angle = generateValueCode(block.getInputTargetBlock('ANGLE')) || '90';
code += `servo_${pinServo}.write(${angle});\n`;
break;
case 'ultrasonic_read':
const trig = block.getFieldValue('TRIG');
const echo = block.getFieldValue('ECHO');
code += `digitalWrite(${trig}, LOW);\n`;
code += `delayMicroseconds(2);\n`;
code += `digitalWrite(${trig}, HIGH);\n`;
code += `delayMicroseconds(10);\n`;
code += `digitalWrite(${trig}, LOW);\n`;
code += `duration_${trig} = pulseIn(${echo}, HIGH);\n`;
code += `distance_${trig} = duration_${trig} * 0.034 / 2;\n`;
break;
case 'lcd_print':
const cursorCol = block.getFieldValue('CURSOR_COL');
const cursorRow = block.getFieldValue('CURSOR_ROW');
let textLcd = generateValueCode(block.getInputTargetBlock('TEXT')) || '"Hello World!!"';
if (block.getInputTargetBlock('TEXT')?.type !== 'serial_read_string') {
textLcd = cleanString(textLcd);
}
code += `lcd.setCursor(${cursorCol}, ${cursorRow});\nlcd.print(${textLcd});\n`;
break;
case 'lcd_print_value':
const cursorColVal = block.getFieldValue('CURSOR_COL');
const cursorRowVal = block.getFieldValue('CURSOR_ROW');
const valueLcd = generateValueCode(block.getInputTargetBlock('VALUE')) || '0';
code += `lcd.setCursor(${cursorColVal}, ${cursorRowVal});\nlcd.print(${valueLcd});\n`;
break;
case 'lcd_clear':
code += `lcd.clear();\n`;
break;
case 'serial_begin':
const baud = block.getFieldValue('BAUD');
code += `Serial.begin(${baud});\n`;
break;
case 'serial_print':
let textSerial = generateValueCode(block.getInputTargetBlock('TEXT')) || '""';
if (block.getInputTargetBlock('TEXT')?.type !== 'serial_read_string') {
textSerial = cleanString(textSerial);
}
code += `Serial.print(${textSerial});\n`;
break;
case 'serial_print_value':
const valueSerial = generateValueCode(block.getInputTargetBlock('VALUE')) || '0';
code += `Serial.print(${valueSerial});\n`;
break;
case 'serial_println':
let textSerialLn = generateValueCode(block.getInputTargetBlock('TEXT')) || '""';
if (block.getInputTargetBlock('TEXT')?.type !== 'serial_read_string') {
textSerialLn = cleanString(textSerialLn);
}
code += `Serial.println(${textSerialLn});\n`;
break;
case 'serial_println_value':
const valueSerialLn = generateValueCode(block.getInputTargetBlock('VALUE')) || '0';
code += `Serial.println(${valueSerialLn});\n`;
break;
case 'custom_code':
const customCode = block.getFieldValue('CODE');
code += `${customCode}\n`;
break;
case 'seven_segment_display':
const pinA = block.getFieldValue('PIN_A');
const pinB = block.getFieldValue('PIN_B');
const pinC = block.getFieldValue('PIN_C');
const pinD = block.getFieldValue('PIN_D');
const pinE = block.getFieldValue('PIN_E');
const pinF = block.getFieldValue('PIN_F');
const pinG = block.getFieldValue('PIN_G');
const number = generateValueCode(block.getInputTargetBlock('NUMBER')) || '0';
code += `if (${number} == -1) {\n`;
code += ` digitalWrite(${pinA}, LOW);\n`;
code += ` digitalWrite(${pinB}, LOW);\n`;
code += ` digitalWrite(${pinC}, LOW);\n`;
code += ` digitalWrite(${pinD}, LOW);\n`;
code += ` digitalWrite(${pinE}, LOW);\n`;
code += ` digitalWrite(${pinF}, LOW);\n`;
code += ` digitalWrite(${pinG}, LOW);\n`;
code += `} else {\n`;
code += ` int digit = constrain(${number}, 0, 9);\n`;
code += ` digitalWrite(${pinA}, ${segmentPatterns[0][0] ? 'HIGH' : 'LOW'});\n`;
code += ` digitalWrite(${pinB}, ${segmentPatterns[0][1] ? 'HIGH' : 'LOW'});\n`;
code += ` digitalWrite(${pinC}, ${segmentPatterns[0][2] ? 'HIGH' : 'LOW'});\n`;
code += ` digitalWrite(${pinD}, ${segmentPatterns[0][3] ? 'HIGH' : 'LOW'});\n`;
code += ` digitalWrite(${pinE}, ${segmentPatterns[0][4] ? 'HIGH' : 'LOW'});\n`;
code += ` digitalWrite(${pinF}, ${segmentPatterns[0][5] ? 'HIGH' : 'LOW'});\n`;
code += ` digitalWrite(${pinG}, ${segmentPatterns[0][6] ? 'HIGH' : 'LOW'});\n`;
code += ` if (digit == 1) { digitalWrite(${pinA}, ${segmentPatterns[1][0] ? 'HIGH' : 'LOW'}); digitalWrite(${pinB}, ${segmentPatterns[1][1] ? 'HIGH' : 'LOW'}); digitalWrite(${pinC}, ${segmentPatterns[1][2] ? 'HIGH' : 'LOW'}); digitalWrite(${pinD}, ${segmentPatterns[1][3] ? 'HIGH' : 'LOW'}); digitalWrite(${pinE}, ${segmentPatterns[1][4] ? 'HIGH' : 'LOW'}); digitalWrite(${pinF}, ${segmentPatterns[1][5] ? 'HIGH' : 'LOW'}); digitalWrite(${pinG}, ${segmentPatterns[1][6] ? 'HIGH' : 'LOW'}); }\n`;
code += ` else if (digit == 2) { digitalWrite(${pinA}, ${segmentPatterns[2][0] ? 'HIGH' : 'LOW'}); digitalWrite(${pinB}, ${segmentPatterns[2][1] ? 'HIGH' : 'LOW'}); digitalWrite(${pinC}, ${segmentPatterns[2][2] ? 'HIGH' : 'LOW'}); digitalWrite(${pinD}, ${segmentPatterns[2][3] ? 'HIGH' : 'LOW'}); digitalWrite(${pinE}, ${segmentPatterns[2][4] ? 'HIGH' : 'LOW'}); digitalWrite(${pinF}, ${segmentPatterns[2][5] ? 'HIGH' : 'LOW'}); digitalWrite(${pinG}, ${segmentPatterns[2][6] ? 'HIGH' : 'LOW'}); }\n`;
code += ` else if (digit == 3) { digitalWrite(${pinA}, ${segmentPatterns[3][0] ? 'HIGH' : 'LOW'}); digitalWrite(${pinB}, ${segmentPatterns[3][1] ? 'HIGH' : 'LOW'}); digitalWrite(${pinC}, ${segmentPatterns[3][2] ? 'HIGH' : 'LOW'}); digitalWrite(${pinD}, ${segmentPatterns[3][3] ? 'HIGH' : 'LOW'}); digitalWrite(${pinE}, ${segmentPatterns[3][4] ? 'HIGH' : 'LOW'}); digitalWrite(${pinF}, ${segmentPatterns[3][5] ? 'HIGH' : 'LOW'}); digitalWrite(${pinG}, ${segmentPatterns[3][6] ? 'HIGH' : 'LOW'}); }\n`;
code += ` else if (digit == 4) { digitalWrite(${pinA}, ${segmentPatterns[4][0] ? 'HIGH' : 'LOW'}); digitalWrite(${pinB}, ${segmentPatterns[4][1] ? 'HIGH' : 'LOW'}); digitalWrite(${pinC}, ${segmentPatterns[4][2] ? 'HIGH' : 'LOW'}); digitalWrite(${pinD}, ${segmentPatterns[4][3] ? 'HIGH' : 'LOW'}); digitalWrite(${pinE}, ${segmentPatterns[4][4] ? 'HIGH' : 'LOW'}); digitalWrite(${pinF}, ${segmentPatterns[4][5] ? 'HIGH' : 'LOW'}); digitalWrite(${pinG}, ${segmentPatterns[4][6] ? 'HIGH' : 'LOW'}); }\n`;
code += ` else if (digit == 5) { digitalWrite(${pinA}, ${segmentPatterns[5][0] ? 'HIGH' : 'LOW'}); digitalWrite(${pinB}, ${segmentPatterns[5][1] ? 'HIGH' : 'LOW'}); digitalWrite(${pinC}, ${segmentPatterns[5][2] ? 'HIGH' : 'LOW'}); digitalWrite(${pinD}, ${segmentPatterns[5][3] ? 'HIGH' : 'LOW'}); digitalWrite(${pinE}, ${segmentPatterns[5][4] ? 'HIGH' : 'LOW'}); digitalWrite(${pinF}, ${segmentPatterns[5][5] ? 'HIGH' : 'LOW'}); digitalWrite(${pinG}, ${segmentPatterns[5][6] ? 'HIGH' : 'LOW'}); }\n`;
code += ` else if (digit == 6) { digitalWrite(${pinA}, ${segmentPatterns[6][0] ? 'HIGH' : 'LOW'}); digitalWrite(${pinB}, ${segmentPatterns[6][1] ? 'HIGH' : 'LOW'}); digitalWrite(${pinC}, ${segmentPatterns[6][2] ? 'HIGH' : 'LOW'}); digitalWrite(${pinD}, ${segmentPatterns[6][3] ? 'HIGH' : 'LOW'}); digitalWrite(${pinE}, ${segmentPatterns[6][4] ? 'HIGH' : 'LOW'}); digitalWrite(${pinF}, ${segmentPatterns[6][5] ? 'HIGH' : 'LOW'}); digitalWrite(${pinG}, ${segmentPatterns[6][6] ? 'HIGH' : 'LOW'}); }\n`;
code += ` else if (digit == 7) { digitalWrite(${pinA}, ${segmentPatterns[7][0] ? 'HIGH' : 'LOW'}); digitalWrite(${pinB}, ${segmentPatterns[7][1] ? 'HIGH' : 'LOW'}); digitalWrite(${pinC}, ${segmentPatterns[7][2] ? 'HIGH' : 'LOW'}); digitalWrite(${pinD}, ${segmentPatterns[7][3] ? 'HIGH' : 'LOW'}); digitalWrite(${pinE}, ${segmentPatterns[7][4] ? 'HIGH' : 'LOW'}); digitalWrite(${pinF}, ${segmentPatterns[7][5] ? 'HIGH' : 'LOW'}); digitalWrite(${pinG}, ${segmentPatterns[7][6] ? 'HIGH' : 'LOW'}); }\n`;
code += ` else if (digit == 8) { digitalWrite(${pinA}, ${segmentPatterns[8][0] ? 'HIGH' : 'LOW'}); digitalWrite(${pinB}, ${segmentPatterns[8][1] ? 'HIGH' : 'LOW'}); digitalWrite(${pinC}, ${segmentPatterns[8][2] ? 'HIGH' : 'LOW'}); digitalWrite(${pinD}, ${segmentPatterns[8][3] ? 'HIGH' : 'LOW'}); digitalWrite(${pinE}, ${segmentPatterns[8][4] ? 'HIGH' : 'LOW'}); digitalWrite(${pinF}, ${segmentPatterns[8][5] ? 'HIGH' : 'LOW'}); digitalWrite(${pinG}, ${segmentPatterns[8][6] ? 'HIGH' : 'LOW'}); }\n`;
code += ` else if (digit == 9) { digitalWrite(${pinA}, ${segmentPatterns[9][0] ? 'HIGH' : 'LOW'}); digitalWrite(${pinB}, ${segmentPatterns[9][1] ? 'HIGH' : 'LOW'}); digitalWrite(${pinC}, ${segmentPatterns[9][2] ? 'HIGH' : 'LOW'}); digitalWrite(${pinD}, ${segmentPatterns[9][3] ? 'HIGH' : 'LOW'}); digitalWrite(${pinE}, ${segmentPatterns[9][4] ? 'HIGH' : 'LOW'}); digitalWrite(${pinF}, ${segmentPatterns[9][5] ? 'HIGH' : 'LOW'}); digitalWrite(${pinG}, ${segmentPatterns[9][6] ? 'HIGH' : 'LOW'}); }\n`;
code += `}\n`;
break;
case 'tone':
const tonePin = block.getFieldValue('PIN');
const frequency = generateValueCode(block.getInputTargetBlock('FREQUENCY')) || '440';
code += `tone(${tonePin}, ${frequency});\n`;
break;
case 'noTone':
const noTonePin = block.getFieldValue('PIN');
code += `noTone(${noTonePin});\n`;
break;
}
block = block.getNextBlock();
}
return code;
}
workspace.addChangeListener(function (event) {
const arduinoCode = generateArduinoCode();
document.getElementById('generated-code').textContent = arduinoCode;
if (event.type === Blockly.Events.BLOCK_CREATE ||
event.type === Blockly.Events.BLOCK_DELETE ||
event.type === Blockly.Events.BLOCK_MOVE ||
event.type === Blockly.Events.BLOCK_CHANGE) {
saveWorkspaceToUrl();
}
});
document.getElementById('clear-btn').addEventListener('click', function () {
workspace.clear();
document.getElementById('generated-code').textContent = '// 生成されたArduinoコードがここに表示されます';
const url = new URL(window.location);
url.searchParams.delete('blocks');
history.pushState({}, '', url);
});
document.getElementById('save-btn').addEventListener('click', function () {
const url = window.location.href;
navigator.clipboard.writeText(url).then(() => {
alert('URLがクリップボードにコピーされました!');
}).catch(err => {
console.error('URLのコピーに失敗しました:', err);
});
});
document.getElementById('download-btn').addEventListener('click', function () {
const code = document.getElementById('generated-code').textContent;
const now = new Date();
const timestamp = now.getFullYear() +
('0' + (now.getMonth() + 1)).slice(-2) +
('0' + now.getDate()).slice(-2) +
('0' + now.getHours()).slice(-2) +
('0' + now.getMinutes()).slice(-2);
const filename = `${timestamp}.ino`;
const blob = new Blob([code], { type: 'text/x-arduino' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
});
document.getElementById('print-btn').addEventListener('click', function () {
html2canvas(document.getElementById('block-area')).then(canvas => {
const image = canvas.toDataURL('image/png');
const printWindow = window.open('');
printWindow.document.write('<img src="' + image + '" onload="window.print();window.close()" />');
printWindow.document.close();
});
});
// シリアル通信ボタンのイベントリスナー
document.getElementById('serial-btn').addEventListener('click', function () {
const modal = document.getElementById('serial-modal');
const iframe = document.getElementById('serial-iframe');
iframe.src = 'pv17-3.html'; // index2.htmlを読み込む
modal.style.display = 'block'; // モーダルを表示
});
// 閉じるボタンのイベントリスナー
document.getElementById('close-modal').addEventListener('click', function () {
const modal = document.getElementById('serial-modal');
const iframe = document.getElementById('serial-iframe');
modal.style.display = 'none'; // モーダルを非表示
iframe.src = ''; // iframeの内容をリセット
});
document.getElementById('exit-btn').addEventListener('click', function () {
window.location.href = 'https://huggingface.co/ek15072809';
});
loadWorkspaceFromUrl();
</script>
</body>
</html>