esp32blockly/src/blocks/arduino_generators.js

442 lines
18 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { arduinoGenerator, Order } from '../generators/arduino.js';
// ─── Pin I/O ──────────────────────────────────────────────
arduinoGenerator.forBlock['pin_set_mode'] = function (block) {
const pin = block.getFieldValue('PIN');
const mode = block.getFieldValue('MODE');
const modeMap = { OUT: 'OUTPUT', IN: 'INPUT', IN_PULLUP: 'INPUT_PULLUP' };
arduinoGenerator.addSetupCode(`pinMode(${pin}, ${modeMap[mode] || 'OUTPUT'});`);
return '';
};
arduinoGenerator.forBlock['pin_digital_write'] = function (block) {
const pin = block.getFieldValue('PIN');
const value = block.getFieldValue('VALUE');
return `digitalWrite(${pin}, ${value === '1' ? 'HIGH' : 'LOW'});\n`;
};
arduinoGenerator.forBlock['pin_digital_read'] = function (block) {
const pin = block.getFieldValue('PIN');
return [`digitalRead(${pin})`, Order.FUNCTION_CALL];
};
// ─── PWM ──────────────────────────────────────────────────
arduinoGenerator.forBlock['pwm_init'] = function () {
return '';
};
arduinoGenerator.forBlock['pwm_set_duty'] = function (block) {
const pin = block.getFieldValue('PIN');
const duty = arduinoGenerator.valueToCode(block, 'DUTY', Order.NONE) || '0';
return `analogWrite(${pin}, ${duty});\n`;
};
arduinoGenerator.forBlock['pwm_set_freq'] = function () {
return '// PWM frequency change not supported on AVR\n';
};
// ─── ADC ──────────────────────────────────────────────────
arduinoGenerator.forBlock['adc_read'] = function (block) {
const pin = block.getFieldValue('PIN');
return [`analogRead(${pin})`, Order.FUNCTION_CALL];
};
// ─── Sensors (sonar / HC-SR04) ────────────────────────────
arduinoGenerator.forBlock['sonar_distance'] = function (block) {
const trig = arduinoGenerator.valueToCode(block, 'TRIG', Order.NONE) || '2';
const echo = arduinoGenerator.valueToCode(block, 'ECHO', Order.NONE) || '3';
arduinoGenerator.addDeclaration('sonar_helper',
'float _sonar_cm(int trigPin, int echoPin) {\n' +
' pinMode(trigPin, OUTPUT);\n' +
' pinMode(echoPin, INPUT);\n' +
' digitalWrite(trigPin, LOW);\n' +
' delayMicroseconds(2);\n' +
' digitalWrite(trigPin, HIGH);\n' +
' delayMicroseconds(10);\n' +
' digitalWrite(trigPin, LOW);\n' +
' long d = pulseIn(echoPin, HIGH, 30000);\n' +
' return d > 0 ? d / 58.0 : -1;\n' +
'}'
);
return [`_sonar_cm(${trig}, ${echo})`, Order.FUNCTION_CALL];
};
// ─── Time ─────────────────────────────────────────────────
arduinoGenerator.forBlock['sleep_seconds'] = function (block) {
const seconds = arduinoGenerator.valueToCode(block, 'SECONDS', Order.MULTIPLICATIVE) || '1';
return `delay(${seconds} * 1000);\n`;
};
arduinoGenerator.forBlock['sleep_ms'] = function (block) {
const ms = arduinoGenerator.valueToCode(block, 'MS', Order.NONE) || '100';
return `delay(${ms});\n`;
};
arduinoGenerator.forBlock['ticks_ms'] = function () {
return ['millis()', Order.FUNCTION_CALL];
};
// ─── Random ───────────────────────────────────────────────
arduinoGenerator.forBlock['random_int'] = function (block) {
const from_ = arduinoGenerator.valueToCode(block, 'FROM', Order.NONE) || '0';
const to = arduinoGenerator.valueToCode(block, 'TO', Order.ADDITIVE) || '10';
return [`random(${from_}, ${to} + 1)`, Order.FUNCTION_CALL];
};
arduinoGenerator.forBlock['random_float'] = function () {
return ['(random(10001) / 10000.0)', Order.MULTIPLICATIVE];
};
arduinoGenerator.forBlock['random_uniform'] = function (block) {
const low = arduinoGenerator.valueToCode(block, 'LOW', Order.NONE) || '0';
const high = arduinoGenerator.valueToCode(block, 'HIGH', Order.NONE) || '1';
return [`(${low} + (random(10001) / 10000.0) * (${high} - ${low}))`, Order.ADDITIVE];
};
arduinoGenerator.forBlock['random_seed'] = function (block) {
const seed = arduinoGenerator.valueToCode(block, 'SEED', Order.NONE) || '0';
return `randomSeed(${seed});\n`;
};
// ─── Print ────────────────────────────────────────────────
arduinoGenerator.forBlock['print_text'] = function (block) {
arduinoGenerator.addSetupCode('Serial.begin(9600);');
const text = arduinoGenerator.valueToCode(block, 'TEXT', Order.NONE) || '""';
return `Serial.println(${text});\n`;
};
// ─── H-Bridge Motor ─────────────────────────────────────
arduinoGenerator.definitions_['hb_set_motor_fn'] = [
'void _hb_set_motor(int p1, int p2, int speed) {',
' speed = constrain(speed, -255, 255);',
' if (speed > 0) { analogWrite(p1, abs(speed)); analogWrite(p2, 0); }',
' else if (speed < 0) { analogWrite(p1, 0); analogWrite(p2, abs(speed)); }',
' else { analogWrite(p1, 0); analogWrite(p2, 0); }',
'}',
].join('\n');
arduinoGenerator.forBlock['hbridge_motor_init'] = function (block) {
var m = block.getFieldValue('MOTOR').toLowerCase();
var in1 = block.getFieldValue('IN1');
var in2 = block.getFieldValue('IN2');
arduinoGenerator.definitions_['hb_motor_' + m + '_in1'] = 'const int motor_' + m + '_in1 = ' + in1 + ';';
arduinoGenerator.definitions_['hb_motor_' + m + '_in2'] = 'const int motor_' + m + '_in2 = ' + in2 + ';';
arduinoGenerator.addSetupCode('pinMode(' + in1 + ', OUTPUT);');
arduinoGenerator.addSetupCode('pinMode(' + in2 + ', OUTPUT);');
return '';
};
arduinoGenerator.forBlock['hbridge_motor_speed'] = function (block) {
var m = block.getFieldValue('MOTOR').toLowerCase();
var speed = arduinoGenerator.valueToCode(block, 'SPEED', Order.NONE) || '0';
return '_hb_set_motor(motor_' + m + '_in1, motor_' + m + '_in2, ' + speed + ');\n';
};
arduinoGenerator.forBlock['hbridge_motor_stop'] = function (block) {
var m = block.getFieldValue('MOTOR').toLowerCase();
return '_hb_set_motor(motor_' + m + '_in1, motor_' + m + '_in2, 0);\n';
};
arduinoGenerator.forBlock['hbridge_dual_init'] = function (block) {
var pins = [
['motor_l_in1', 'L_IN1'], ['motor_l_in2', 'L_IN2'],
['motor_r_in1', 'R_IN1'], ['motor_r_in2', 'R_IN2'],
];
for (var [name, field] of pins) {
var pin = block.getFieldValue(field);
arduinoGenerator.definitions_['hb_' + name] = 'const int ' + name + ' = ' + pin + ';';
arduinoGenerator.addSetupCode('pinMode(' + pin + ', OUTPUT);');
}
return '';
};
arduinoGenerator.forBlock['hbridge_dual_speed'] = function (block) {
var left = arduinoGenerator.valueToCode(block, 'LEFT', Order.NONE) || '0';
var right = arduinoGenerator.valueToCode(block, 'RIGHT', Order.NONE) || '0';
return '_hb_set_motor(motor_l_in1, motor_l_in2, ' + left + ');\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, ' + right + ');\n';
};
arduinoGenerator.forBlock['hbridge_dual_forward'] = function () {
return '_hb_set_motor(motor_l_in1, motor_l_in2, 128);\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, 128);\n';
};
arduinoGenerator.forBlock['hbridge_dual_backward'] = function () {
return '_hb_set_motor(motor_l_in1, motor_l_in2, -128);\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, -128);\n';
};
arduinoGenerator.forBlock['hbridge_dual_left'] = function () {
return '_hb_set_motor(motor_l_in1, motor_l_in2, -128);\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, 128);\n';
};
arduinoGenerator.forBlock['hbridge_dual_right'] = function () {
return '_hb_set_motor(motor_l_in1, motor_l_in2, 128);\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, -128);\n';
};
arduinoGenerator.forBlock['hbridge_dual_forward_x_seconds'] = function (block) {
var s = arduinoGenerator.valueToCode(block, 'SECS', Order.NONE) || '1';
return '_hb_set_motor(motor_l_in1, motor_l_in2, 128);\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, 128);\n' +
'delay((' + s + ') * 1000);\n' +
'_hb_set_motor(motor_l_in1, motor_l_in2, 0);\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, 0);\n';
};
arduinoGenerator.forBlock['hbridge_dual_backward_x_seconds'] = function (block) {
var s = arduinoGenerator.valueToCode(block, 'SECS', Order.NONE) || '1';
return '_hb_set_motor(motor_l_in1, motor_l_in2, -128);\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, -128);\n' +
'delay((' + s + ') * 1000);\n' +
'_hb_set_motor(motor_l_in1, motor_l_in2, 0);\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, 0);\n';
};
arduinoGenerator.forBlock['hbridge_dual_left_x_seconds'] = function (block) {
var s = arduinoGenerator.valueToCode(block, 'SECS', Order.NONE) || '1';
return '_hb_set_motor(motor_l_in1, motor_l_in2, -128);\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, 128);\n' +
'delay((' + s + ') * 1000);\n' +
'_hb_set_motor(motor_l_in1, motor_l_in2, 0);\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, 0);\n';
};
arduinoGenerator.forBlock['hbridge_dual_right_x_seconds'] = function (block) {
var s = arduinoGenerator.valueToCode(block, 'SECS', Order.NONE) || '1';
return '_hb_set_motor(motor_l_in1, motor_l_in2, 128);\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, -128);\n' +
'delay((' + s + ') * 1000);\n' +
'_hb_set_motor(motor_l_in1, motor_l_in2, 0);\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, 0);\n';
};
arduinoGenerator.forBlock['hbridge_dual_stop'] = function () {
return '_hb_set_motor(motor_l_in1, motor_l_in2, 0);\n' +
'_hb_set_motor(motor_r_in1, motor_r_in2, 0);\n';
};
// ─── Sound (shared blocks) ────────────────────────────────
arduinoGenerator.forBlock['sound_play_tone'] = function (block) {
const pin = block.getFieldValue('PIN');
const freq = arduinoGenerator.valueToCode(block, 'FREQ', Order.NONE) || '440';
const duration = arduinoGenerator.valueToCode(block, 'DURATION', Order.NONE) || '500';
return `tone(${pin}, ${freq}, ${duration});\n`;
};
arduinoGenerator.forBlock['sound_stop'] = function (block) {
const pin = block.getFieldValue('PIN');
return `noTone(${pin});\n`;
};
arduinoGenerator.forBlock['sound_play_tone_speaker'] = function () {
return '// Built-in speaker not available on Arduino\n';
};
arduinoGenerator.forBlock['sound_stop_speaker'] = function () {
return '// Built-in speaker not available on Arduino\n';
};
arduinoGenerator.forBlock['sound_play_note'] = function (block) {
const pin = block.getFieldValue('PIN');
const freq = block.getFieldValue('NOTE');
const duration = arduinoGenerator.valueToCode(block, 'DURATION', Order.NONE) || '500';
return `tone(${pin}, ${freq}, ${duration});\n`;
};
arduinoGenerator.forBlock['sound_play_note_speaker'] = function () {
return '// Built-in speaker not available on Arduino\n';
};
arduinoGenerator.forBlock['sound_play_staff_note'] =
arduinoGenerator.forBlock['sound_play_note'];
arduinoGenerator.forBlock['sound_play_staff_note_speaker'] =
arduinoGenerator.forBlock['sound_play_note_speaker'];
// ─── Sound buzzer (init + pin-free) ────────────────────
arduinoGenerator.forBlock['sound_buzzer_init'] = function (block) {
const pin = block.getFieldValue('PIN');
arduinoGenerator.definitions_['buzzer_pin'] = `const int _buzzerPin = ${pin};`;
arduinoGenerator.addSetupCode('noTone(_buzzerPin);');
return '';
};
arduinoGenerator.forBlock['sound_buzzer_tone'] = function (block) {
const freq = arduinoGenerator.valueToCode(block, 'FREQ', Order.NONE) || '440';
const duration = arduinoGenerator.valueToCode(block, 'DURATION', Order.NONE) || '500';
return `tone(_buzzerPin, ${freq}, ${duration});\n`;
};
arduinoGenerator.forBlock['sound_buzzer_note'] = function (block) {
const freq = block.getFieldValue('NOTE');
const duration = arduinoGenerator.valueToCode(block, 'DURATION', Order.NONE) || '500';
return `tone(_buzzerPin, ${freq}, ${duration});\n`;
};
arduinoGenerator.forBlock['sound_buzzer_staff_note'] =
arduinoGenerator.forBlock['sound_buzzer_note'];
arduinoGenerator.forBlock['sound_buzzer_stop'] = function () {
return 'noTone(_buzzerPin);\n';
};
// ─── Sound melodies ────────────────────────────────────
const MELODIES = {
mario: [
[660,100],[660,100],[0,100],[660,100],[0,100],[520,100],[660,100],[0,100],
[784,100],[0,300],[392,100],[0,300],
[520,100],[0,200],[392,100],[0,200],[330,100],[0,200],
[440,100],[0,100],[494,100],[0,100],[466,100],[440,100],[0,100],
[392,100],[660,100],[784,100],[880,100],[0,100],[698,100],[784,100],
[0,100],[660,100],[0,100],[520,100],[587,100],[494,100],[0,100],
[520,100],[0,200],[392,100],[0,200],[330,100],[0,200],
[440,100],[0,100],[494,100],[0,100],[466,100],[440,100],[0,100],
[392,100],[660,100],[784,100],[880,100],[0,100],[698,100],[784,100],
[0,100],[660,100],[0,100],[520,100],[587,100],[494,100],[0,200],
],
happy: [[523,150],[0,30],[659,150],[0,30],[784,200]],
sad: [[392,250],[0,30],[330,250],[0,30],[262,300]],
};
function melodyArduino(name, pinExpr) {
const notes = MELODIES[name];
const freqArr = `_${name}Freqs`;
const durArr = `_${name}Durs`;
const lenVar = `_${name}Len`;
arduinoGenerator.definitions_[`${name}_freqs`] =
`const int ${freqArr}[] = {${notes.map(([f]) => f).join(',')}};`;
arduinoGenerator.definitions_[`${name}_durs`] =
`const int ${durArr}[] = {${notes.map(([, d]) => d).join(',')}};`;
arduinoGenerator.definitions_[`${name}_len`] =
`const int ${lenVar} = ${notes.length};`;
let code = '';
code += `for (int _i = 0; _i < ${lenVar}; _i++) {\n`;
code += ` if (${freqArr}[_i] == 0) {\n`;
code += ` noTone(${pinExpr});\n`;
code += ` } else {\n`;
code += ` tone(${pinExpr}, ${freqArr}[_i]);\n`;
code += ` }\n`;
code += ` delay(${durArr}[_i]);\n`;
code += `}\n`;
code += `noTone(${pinExpr});\n`;
return code;
}
function melodyPinArduino(name) {
return function (block) {
return melodyArduino(name, String(block.getFieldValue('PIN')));
};
}
function melodyBuzzerArduino(name) {
return function () {
return melodyArduino(name, '_buzzerPin');
};
}
arduinoGenerator.forBlock['sound_melody_mario'] = melodyPinArduino('mario');
arduinoGenerator.forBlock['sound_melody_mario_buzzer'] = melodyBuzzerArduino('mario');
arduinoGenerator.forBlock['sound_melody_happy'] = melodyPinArduino('happy');
arduinoGenerator.forBlock['sound_melody_happy_buzzer'] = melodyBuzzerArduino('happy');
arduinoGenerator.forBlock['sound_melody_sad'] = melodyPinArduino('sad');
arduinoGenerator.forBlock['sound_melody_sad_buzzer'] = melodyBuzzerArduino('sad');
// ─── Arduino-specific blocks ──────────────────────────────
arduinoGenerator.forBlock['arduino_builtin_led'] = function (block) {
const state = block.getFieldValue('STATE');
arduinoGenerator.addSetupCode('pinMode(LED_BUILTIN, OUTPUT);');
return `digitalWrite(LED_BUILTIN, ${state === '1' ? 'HIGH' : 'LOW'});\n`;
};
arduinoGenerator.forBlock['arduino_analog_write'] = function (block) {
const pin = block.getFieldValue('PIN');
const value = arduinoGenerator.valueToCode(block, 'VALUE', Order.NONE) || '0';
return `analogWrite(${pin}, ${value});\n`;
};
arduinoGenerator.forBlock['arduino_analog_read'] = function (block) {
const ch = block.getFieldValue('CHANNEL');
return [`analogRead(A${ch})`, Order.FUNCTION_CALL];
};
arduinoGenerator.forBlock['arduino_servo_attach'] = function (block) {
const pin = block.getFieldValue('PIN');
arduinoGenerator.addInclude('include_servo', '#include <Servo.h>');
arduinoGenerator.addDeclaration(`servo_decl_${pin}`, `Servo servo_${pin};`);
arduinoGenerator.addSetupCode(`servo_${pin}.attach(${pin});`);
return '';
};
arduinoGenerator.forBlock['arduino_servo_write'] = function (block) {
const pin = block.getFieldValue('PIN');
const angle = arduinoGenerator.valueToCode(block, 'ANGLE', Order.NONE) || '90';
arduinoGenerator.addInclude('include_servo', '#include <Servo.h>');
arduinoGenerator.addDeclaration(`servo_decl_${pin}`, `Servo servo_${pin};`);
arduinoGenerator.addSetupCode(`servo_${pin}.attach(${pin});`);
return `servo_${pin}.write(${angle});\n`;
};
arduinoGenerator.forBlock['arduino_tone'] = function (block) {
const pin = block.getFieldValue('PIN');
const freq = arduinoGenerator.valueToCode(block, 'FREQ', Order.NONE) || '440';
const duration = arduinoGenerator.valueToCode(block, 'DURATION', Order.NONE) || '500';
return `tone(${pin}, ${freq}, ${duration});\n`;
};
arduinoGenerator.forBlock['arduino_no_tone'] = function (block) {
const pin = block.getFieldValue('PIN');
return `noTone(${pin});\n`;
};
arduinoGenerator.forBlock['arduino_map'] = function (block) {
const value = arduinoGenerator.valueToCode(block, 'VALUE', Order.NONE) || '0';
const fromLow = arduinoGenerator.valueToCode(block, 'FROM_LOW', Order.NONE) || '0';
const fromHigh = arduinoGenerator.valueToCode(block, 'FROM_HIGH', Order.NONE) || '1023';
const toLow = arduinoGenerator.valueToCode(block, 'TO_LOW', Order.NONE) || '0';
const toHigh = arduinoGenerator.valueToCode(block, 'TO_HIGH', Order.NONE) || '255';
return [`map(${value}, ${fromLow}, ${fromHigh}, ${toLow}, ${toHigh})`, Order.FUNCTION_CALL];
};
// ─── Blocks not applicable to Arduino ─────────────────────
const unsupportedBlocks = [
'wifi_connect', 'wifi_get_ip',
'neopixel_init', 'neopixel_set_color', 'neopixel_show',
'colour_rgb', 'tuple_create_3', 'tuple_index',
'i2c_init', 'i2c_scan', 'i2c_writeto', 'i2c_readfrom', 'tcs34725_read_colours',
'hid_key_press', 'hid_key_down', 'hid_key_up', 'hid_keyboard_type',
'hid_mouse_move', 'hid_mouse_click', 'hid_mouse_scroll',
'hid_gamepad_button', 'hid_gamepad_axis',
'microbit_display_all',
'superbit_motor_run', 'superbit_motor_stop_all',
];
for (const blockType of unsupportedBlocks) {
arduinoGenerator.forBlock[blockType] = function () {
return '// Block not supported on Arduino\n';
};
}
// ─── Loops ─────────────────────────────────────────────
arduinoGenerator.forBlock['controls_repeat_forever'] = function (block) {
let branch = arduinoGenerator.statementToCode(block, 'DO');
return `while (true) {\n${branch}}\n`;
};