| |
| <!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> |