|
|
|
|
|
<!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; |
|
|
} |
|
|
|
|
|
|
|
|
.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> |
|
|
|
|
|
|
|
|
<script src="./blockly.min.js"></script> |
|
|
|
|
|
<script src="./ja.js"></script> |
|
|
|
|
|
<script src="./pako.min.js"></script> |
|
|
|
|
|
<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> |