168 lines
5.2 KiB
JavaScript
168 lines
5.2 KiB
JavaScript
// h-bridge Motor Driver addon
|
|
//
|
|
// Adds a "h-bridge Motor" toolbox category with:
|
|
// - motor_init: set up a motor with IN1 and IN2 pins
|
|
// - motor_speed: set speed from -255 (full reverse) to +255 (full forward)
|
|
// - motor_stop: stop a motor
|
|
//
|
|
// Generates device-appropriate MicroPython:
|
|
// - ESP32 / RP2040: machine.Pin + machine.PWM
|
|
// - micro:bit: pinN.write_analog()
|
|
|
|
// --- Block definitions ---
|
|
|
|
api.Blockly.Blocks['hbridge_motor_init'] = {
|
|
init() {
|
|
this.appendDummyInput()
|
|
.appendField('init motor')
|
|
.appendField(new api.Blockly.FieldDropdown([
|
|
['A', 'A'],
|
|
['B', 'B'],
|
|
]), 'MOTOR')
|
|
.appendField('IN1 pin')
|
|
.appendField(new api.Blockly.FieldNumber(2, 0, 48, 1), 'IN1')
|
|
.appendField('IN2 pin')
|
|
.appendField(new api.Blockly.FieldNumber(3, 0, 48, 1), 'IN2');
|
|
this.setPreviousStatement(true, null);
|
|
this.setNextStatement(true, null);
|
|
this.setColour(30);
|
|
this.setTooltip('Initialize an h-bridge motor channel with two pins');
|
|
},
|
|
};
|
|
|
|
api.Blockly.Blocks['hbridge_motor_speed'] = {
|
|
init() {
|
|
this.appendDummyInput()
|
|
.appendField('set motor')
|
|
.appendField(new api.Blockly.FieldDropdown([
|
|
['A', 'A'],
|
|
['B', 'B'],
|
|
]), 'MOTOR');
|
|
this.appendValueInput('SPEED')
|
|
.setCheck('Number')
|
|
.appendField('speed');
|
|
this.setInputsInline(true);
|
|
this.setPreviousStatement(true, null);
|
|
this.setNextStatement(true, null);
|
|
this.setColour(30);
|
|
this.setTooltip('Set motor speed: -255 (full reverse) to +255 (full forward)');
|
|
},
|
|
};
|
|
|
|
api.Blockly.Blocks['hbridge_motor_stop'] = {
|
|
init() {
|
|
this.appendDummyInput()
|
|
.appendField('stop motor')
|
|
.appendField(new api.Blockly.FieldDropdown([
|
|
['A', 'A'],
|
|
['B', 'B'],
|
|
]), 'MOTOR');
|
|
this.setPreviousStatement(true, null);
|
|
this.setNextStatement(true, null);
|
|
this.setColour(30);
|
|
this.setTooltip('Stop an h-bridge motor (brake)');
|
|
},
|
|
};
|
|
|
|
// --- Helpers used by generators ---
|
|
|
|
function isMicrobit() {
|
|
return api.getDeviceId() === 'microbit';
|
|
}
|
|
|
|
function mbPin(pin) {
|
|
var n = Math.max(0, Math.min(20, parseInt(pin, 10) || 0));
|
|
return 'pin' + n;
|
|
}
|
|
|
|
// --- Code generators ---
|
|
|
|
api.pythonGenerator.forBlock['hbridge_motor_init'] = function (block) {
|
|
var motor = block.getFieldValue('MOTOR');
|
|
var in1 = block.getFieldValue('IN1');
|
|
var in2 = block.getFieldValue('IN2');
|
|
var m = motor.toLowerCase();
|
|
|
|
if (isMicrobit()) {
|
|
// micro:bit: no explicit init needed, just store pin numbers for later use.
|
|
// We define variables so speed/stop blocks can reference them.
|
|
api.pythonGenerator.definitions_['import_microbit'] = 'from microbit import *';
|
|
api.pythonGenerator.definitions_['hb_motor_' + m + '_pins'] =
|
|
'motor_' + m + '_in1 = ' + mbPin(in1) + '\n' +
|
|
'motor_' + m + '_in2 = ' + mbPin(in2);
|
|
api.pythonGenerator.definitions_['hb_set_motor_mb'] = [
|
|
'def _hb_set_motor(p1, p2, speed):',
|
|
' speed = max(-255, min(255, int(speed)))',
|
|
' duty = abs(speed) * 4',
|
|
' if speed > 0:',
|
|
' p1.write_analog(duty)',
|
|
' p2.write_analog(0)',
|
|
' elif speed < 0:',
|
|
' p1.write_analog(0)',
|
|
' p2.write_analog(duty)',
|
|
' else:',
|
|
' p1.write_analog(0)',
|
|
' p2.write_analog(0)',
|
|
].join('\n');
|
|
} else {
|
|
// ESP32 / RP2040: machine.PWM
|
|
api.pythonGenerator.definitions_['import_machine'] =
|
|
'from machine import Pin, PWM';
|
|
api.pythonGenerator.definitions_['hb_motor_' + m + '_in1'] =
|
|
'motor_' + m + '_in1 = PWM(Pin(' + in1 + '), freq=1000, duty=0)';
|
|
api.pythonGenerator.definitions_['hb_motor_' + m + '_in2'] =
|
|
'motor_' + m + '_in2 = PWM(Pin(' + in2 + '), freq=1000, duty=0)';
|
|
api.pythonGenerator.definitions_['hb_set_motor'] = [
|
|
'def _hb_set_motor(pwm1, pwm2, speed):',
|
|
' speed = max(-255, min(255, int(speed)))',
|
|
' duty = abs(speed) * 4',
|
|
' if speed > 0:',
|
|
' pwm1.duty(duty)',
|
|
' pwm2.duty(0)',
|
|
' elif speed < 0:',
|
|
' pwm1.duty(0)',
|
|
' pwm2.duty(duty)',
|
|
' else:',
|
|
' pwm1.duty(0)',
|
|
' pwm2.duty(0)',
|
|
].join('\n');
|
|
}
|
|
return '';
|
|
};
|
|
|
|
api.pythonGenerator.forBlock['hbridge_motor_speed'] = function (block) {
|
|
var motor = block.getFieldValue('MOTOR');
|
|
var m = motor.toLowerCase();
|
|
var speed = api.pythonGenerator.valueToCode(
|
|
block, 'SPEED', api.pythonGenerator.ORDER_NONE
|
|
) || '0';
|
|
return '_hb_set_motor(motor_' + m + '_in1, motor_' + m + '_in2, ' + speed + ')\n';
|
|
};
|
|
|
|
api.pythonGenerator.forBlock['hbridge_motor_stop'] = function (block) {
|
|
var motor = block.getFieldValue('MOTOR');
|
|
var m = motor.toLowerCase();
|
|
return '_hb_set_motor(motor_' + m + '_in1, motor_' + m + '_in2, 0)\n';
|
|
};
|
|
|
|
// --- Register toolbox category ---
|
|
|
|
api.registerCategories([
|
|
{
|
|
kind: 'category',
|
|
name: 'H-Bridge Motor',
|
|
colour: '30',
|
|
contents: [
|
|
{ kind: 'block', type: 'hbridge_motor_init' },
|
|
{
|
|
kind: 'block',
|
|
type: 'hbridge_motor_speed',
|
|
inputs: {
|
|
SPEED: { shadow: { type: 'math_number', fields: { NUM: 200 } } },
|
|
},
|
|
},
|
|
{ kind: 'block', type: 'hbridge_motor_stop' },
|
|
],
|
|
},
|
|
]);
|