Multiple motor-changes. Changed _isOn to _status to reflect the more advanced states a motor (or port) can be in. Implemented promise-based completion on motor-on-for-rotation-block, since this block requires a callback from the hub. Changed lower threshold for power-bias function. Deleted some outdated todos. Changed connectID-terminology to portID. Implemented handling port output command feedback. Commented out some distance and tilt-related blocks that I think we can do without and that will lower the number of blocks.

This commit is contained in:
Kevin Andersen 2019-02-04 16:30:04 -05:00
parent c03dc60feb
commit 5822f762ca
2 changed files with 98 additions and 84 deletions

47
package-lock.json generated
View file

@ -12112,9 +12112,9 @@
} }
}, },
"scratch-audio": { "scratch-audio": {
"version": "0.1.0-prerelease.20190108181031", "version": "0.1.0-prerelease.20190114210212",
"resolved": "https://registry.npmjs.org/scratch-audio/-/scratch-audio-0.1.0-prerelease.20190108181031.tgz", "resolved": "https://registry.npmjs.org/scratch-audio/-/scratch-audio-0.1.0-prerelease.20190114210212.tgz",
"integrity": "sha512-Ygu+pN2u9det8HTIo+2wj8ibqe0QjAA624N9GxC62nrdGH39NxDRJyiwheeuZH/oEjM9RTsCSSOH+C9fXA9ekA==", "integrity": "sha512-drcAV8HPJYDFPDie5JA6z0wAH6789yf6Uacbzde/WcnpmpQCakgj8Hx/OeI2td3aZXKSq09d09S9WV5LOZpfrw==",
"dev": true, "dev": true,
"requires": { "requires": {
"audio-context": "1.0.1", "audio-context": "1.0.1",
@ -12123,9 +12123,9 @@
} }
}, },
"scratch-blocks": { "scratch-blocks": {
"version": "0.1.0-prerelease.1545364693", "version": "0.1.0-prerelease.1548885087",
"resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.1545364693.tgz", "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.1548885087.tgz",
"integrity": "sha512-juD5kE0Zqbl0O0NaKYL70wgHkSr0z87sKhMz/upPu9GY03N4FNKWsRdq4iWvGKmujEWq8WKe6mK7jRYD5xJu9A==", "integrity": "sha512-NL5HRfk/xux5o6THmfFC/Uit2TskSHIal1V5RpTHknVvNJzvzZk+xFWFrMXFwrPIAJk0jTY8JzIotRcfga/eZg==",
"dev": true, "dev": true,
"requires": { "requires": {
"exports-loader": "0.6.3", "exports-loader": "0.6.3",
@ -12168,9 +12168,9 @@
} }
}, },
"scratch-render": { "scratch-render": {
"version": "0.1.0-prerelease.20190109203013", "version": "0.1.0-prerelease.20190128154859",
"resolved": "https://registry.npmjs.org/scratch-render/-/scratch-render-0.1.0-prerelease.20190109203013.tgz", "resolved": "https://registry.npmjs.org/scratch-render/-/scratch-render-0.1.0-prerelease.20190128154859.tgz",
"integrity": "sha512-yrkBuF1zLHrXEHQmbQhpATDxot/wqoN/oDW2aIjzV9ylxCy+zNdGI2XJ9tRy10bCcM5bdC879ROX9fCr+n6FwQ==", "integrity": "sha512-OO4JwmoI5JaN0Neg5dsYbzJ2xbde97aUn0y7sAqpY/Anyyz1e8Fudz12/YpSd5cXp4QnYESdC49X2uw0ChBb4A==",
"dev": true, "dev": true,
"requires": { "requires": {
"grapheme-breaker": "0.3.2", "grapheme-breaker": "0.3.2",
@ -12180,29 +12180,8 @@
"minilog": "3.1.0", "minilog": "3.1.0",
"raw-loader": "^0.5.1", "raw-loader": "^0.5.1",
"scratch-storage": "^1.0.0", "scratch-storage": "^1.0.0",
"scratch-svg-renderer": "0.2.0-prerelease.20190109201344", "scratch-svg-renderer": "0.2.0-prerelease.20190125192231",
"twgl.js": "4.4.0" "twgl.js": "4.4.0"
},
"dependencies": {
"base64-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz",
"integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==",
"dev": true
},
"scratch-svg-renderer": {
"version": "0.2.0-prerelease.20190109201344",
"resolved": "https://registry.npmjs.org/scratch-svg-renderer/-/scratch-svg-renderer-0.2.0-prerelease.20190109201344.tgz",
"integrity": "sha512-pRMvQrM5UA2wcqleaXVpFx0Pi6Q3GsRA5elJ0tJksdr6k8HYm5D6sW62VtEtMHjnkQDa+EFyqfHq9IEPnzFjeQ==",
"dev": true,
"requires": {
"base64-js": "1.2.1",
"base64-loader": "1.0.0",
"minilog": "3.1.0",
"scratch-render-fonts": "1.0.0-prerelease.20180906193204",
"transformation-matrix": "1.14.1"
}
}
} }
}, },
"scratch-render-fonts": { "scratch-render-fonts": {
@ -12253,9 +12232,9 @@
} }
}, },
"scratch-svg-renderer": { "scratch-svg-renderer": {
"version": "0.2.0-prerelease.20181220183040", "version": "0.2.0-prerelease.20190125192231",
"resolved": "https://registry.npmjs.org/scratch-svg-renderer/-/scratch-svg-renderer-0.2.0-prerelease.20181220183040.tgz", "resolved": "https://registry.npmjs.org/scratch-svg-renderer/-/scratch-svg-renderer-0.2.0-prerelease.20190125192231.tgz",
"integrity": "sha512-RnkoPj6uweqOy46hswanEqw0t21IUYBB9U2SrkYJIw76/BHGSDJnRQxPfKWpUFVb3LaSRdXX9ES6hnyzmi0qhA==", "integrity": "sha512-8cOLJsN2zDT2FLcB3wLxew3tzO6fkI25uiaW0c6juQl5zJseANIvP4tc31gaeUG4xSQa1zfk/PpXKPQDGa66Tw==",
"dev": true, "dev": true,
"requires": { "requires": {
"base64-js": "1.2.1", "base64-js": "1.2.1",

View file

@ -65,6 +65,20 @@ const BoostCommand = {
OUTPUT: 0x81, OUTPUT: 0x81,
}; };
/**
* Enum for ids for various output commands on the Boost.
* @readonly
* @enum {number}
*/
const BoostOutputCommandFeedback = {
// TODO: Figure out if this enum is necessary or if we're always just sending 0x81
BUFFER_EMPTY_COMMAND_IN_PROGRESS: 0x01,
BUFFER_EMPTY_COMMAND_COMPLETED: 0x02,
CURRENT_COMMAND_DISCARDED: 0x04,
IDLE: 0x08,
BUSY_OR_FULL: 0x10,
};
/** /**
* Enum for physical Boost Ports * Enum for physical Boost Ports
@ -205,7 +219,7 @@ class BoostMotor {
* @type {boolean} * @type {boolean}
* @private * @private
*/ */
this._isOn = false; this._status = BoostOutputCommandFeedback.IDLE;
/** /**
* If the motor has been turned on or is actively braking for a specific duration, this is the timeout ID for * If the motor has been turned on or is actively braking for a specific duration, this is the timeout ID for
@ -229,6 +243,14 @@ class BoostMotor {
*/ */
this._pendingTimeoutDelay = null; this._pendingTimeoutDelay = null;
/**
* If the motor has been turned on or is actively braking for a specific duration, this is the timeout ID for
* the end-of-action handler. Cancel this when changing plans.
* @type {Object}
* @private
*/
this._pendingPromiseFunction = null;
this.startBraking = this.startBraking.bind(this); this.startBraking = this.startBraking.bind(this);
this.turnOff = this.turnOff.bind(this); this.turnOff = this.turnOff.bind(this);
} }
@ -271,13 +293,14 @@ class BoostMotor {
*/ */
set power (value) { set power (value) {
const p = Math.max(0, Math.min(value, 100)); const p = Math.max(0, Math.min(value, 100));
// Lego Boost hub only turns motors at power range [30 - 100], so
// map value from [0 - 100] to [30 - 100]. // Lego Boost hub only turns motors at power range [20 - 100], so
// map value from [0 - 100] to [20 - 100].
if (p === 0) { if (p === 0) {
this._power = 0; this._power = 0;
} else { } else {
const delta = 100 / p; const delta = 100 / p;
this._power = 30 + (70 / delta); this._power = 20 + (80 / delta);
} }
} }
@ -292,11 +315,10 @@ class BoostMotor {
* @param {int} value - * @param {int} value -
*/ */
set position (value) { set position (value) {
// Todo: wrap around rotation to avoid extremely large numbers
this._position = value; this._position = value;
} }
/** /**
* @return {int} - * @return {int} -
*/ */
get positionZero () { get positionZero () {
@ -307,7 +329,6 @@ class BoostMotor {
* @param {int} value - * @param {int} value -
*/ */
set positionZero (value) { set positionZero (value) {
// Todo: wrap around rotation to avoid extremely large numbers
this._positionZero = value; this._positionZero = value;
} }
@ -315,7 +336,7 @@ class BoostMotor {
* @return {boolean} - true if this motor is currently moving, false if this motor is off or braking. * @return {boolean} - true if this motor is currently moving, false if this motor is off or braking.
*/ */
get isOn () { get isOn () {
return this._isOn; return this._status;
} }
/** /**
@ -332,6 +353,14 @@ class BoostMotor {
return this._pendingTimeoutDelay; return this._pendingTimeoutDelay;
} }
/**
* @return {boolean} - true if this motor is currently moving, false if this motor is off or braking.
*/
get pendingPromiseFunction () {
return this._pendingPromiseFunction;
}
/** /**
* Turn this motor on indefinitely. * Turn this motor on indefinitely.
*/ */
@ -348,7 +377,7 @@ class BoostMotor {
this._parent.send(BLECharacteristic, cmd); this._parent.send(BLECharacteristic, cmd);
this._isOn = true; this._status = BoostOutputCommandFeedback.BUFFER_EMPTY_COMMAND_IN_PROGRESS;
this._clearTimeout(); this._clearTimeout();
} }
@ -371,11 +400,6 @@ class BoostMotor {
turnOnForDegrees (degrees) { turnOnForDegrees (degrees) {
if (this._power === 0) return; if (this._power === 0) return;
degrees = Math.max(0, degrees); degrees = Math.max(0, degrees);
console.log(degrees)
/* TODO: Position parameter must be given as int32. Convert degrees to int32. */
var buffer = new ArrayBuffer(4)
var dataview = new DataView(buffer)
dataview.setInt32(0, degrees)
const cmd = this._parent.generateOutputCommand( const cmd = this._parent.generateOutputCommand(
this._index, this._index,
@ -388,7 +412,7 @@ class BoostMotor {
0x00,0x03]) 0x00,0x03])
); );
this._isOn = true; this._status = BoostOutputCommandFeedback.BUFFER_EMPTY_COMMAND_IN_PROGRESS;
this._parent.send(BLECharacteristic, cmd); this._parent.send(BLECharacteristic, cmd);
} }
@ -409,7 +433,7 @@ class BoostMotor {
this._parent.send(BLECharacteristic, cmd); this._parent.send(BLECharacteristic, cmd);
this._isOn = false; this._status = BoostOutputCommandFeedback.IDLE;
this._setNewTimeout(this.turnOff, BoostMotor.BRAKE_TIME_MS); this._setNewTimeout(this.turnOff, BoostMotor.BRAKE_TIME_MS);
} }
@ -429,7 +453,7 @@ class BoostMotor {
this._parent.send(BLECharacteristic, cmd, useLimiter); this._parent.send(BLECharacteristic, cmd, useLimiter);
this._isOn = false; this._status = BoostOutputCommandFeedback.IDLE;
} }
/** /**
@ -727,17 +751,17 @@ class Boost {
* *
* This sends a command to the Boost to actuate the specified outputs. * This sends a command to the Boost to actuate the specified outputs.
* *
* @param {number} connectID - the port (Connect ID) to send a command to. * @param {number} portID - the port (Connect ID) to send a command to.
* @param {number} commandID - the id of the byte command. * @param {number} commandID - the id of the byte command.
* @param {number} mode - the mode * @param {number} mode - the mode
* @param {array} values - the list of values to write to the command. * @param {array} values - the list of values to write to the command.
* @return {array} - a generated output command. * @return {array} - a generated output command.
*/ */
generateOutputCommand (connectID, subCommandID = 0x51, mode=null, values = null) { generateOutputCommand (portID, subCommandID = 0x51, mode=null, values = null) {
let command = [0x00, BoostCommand.OUTPUT]; let command = [0x00, BoostCommand.OUTPUT];
if (values) { if (values) {
command = command.concat( command = command.concat(
connectID portID
).concat( ).concat(
0x11 // Execute immediately 0x11 // Execute immediately
); );
@ -765,17 +789,17 @@ class Boost {
* This sends a command to the Boost that sets that input format * This sends a command to the Boost that sets that input format
* of the specified inputs and sets value change notifications. * of the specified inputs and sets value change notifications.
* *
* @param {number} connectID - the port (Connect ID) to send a command to. * @param {number} portID - the port (Connect ID) to send a command to.
* @param {number} mode - the mode of the input sensor. * @param {number} mode - the mode of the input sensor.
* @param {number} delta - the delta change needed to trigger notification. * @param {number} delta - the delta change needed to trigger notification.
* @param {boolean} enableNotifications - whether to enable notifications. * @param {boolean} enableNotifications - whether to enable notifications.
* @return {array} - a generated input command. * @return {array} - a generated input command.
*/ */
generateInputCommand (connectID, mode, delta, enableNotifications) { generateInputCommand (portID, mode, delta, enableNotifications) {
var command = [ var command = [
0x00, // Hub ID 0x00, // Hub ID
0x41, // Message Type (Port Input Format Setup (Single)) 0x41, // Message Type (Port Input Format Setup (Single))
connectID, portID,
mode, mode,
].concat(number2int32array(delta)).concat([ ].concat(number2int32array(delta)).concat([
enableNotifications enableNotifications
@ -818,7 +842,10 @@ class Boost {
* We base our switch-case on Message Type * We base our switch-case on Message Type
*/ */
switch (data[2]) { var messageType = data[2];
var portID = data[3];
switch (messageType) {
case BoostMessageTypes.HUB_ATTACHED_IO: // IO Attach/Detach events case BoostMessageTypes.HUB_ATTACHED_IO: // IO Attach/Detach events
/* /*
@ -829,10 +856,10 @@ class Boost {
switch (data[4]) { switch (data[4]) {
case BoostIOEvent.ATTACHED: case BoostIOEvent.ATTACHED:
//case BoostIOEvent.ATTACHED_VIRTUAL: //case BoostIOEvent.ATTACHED_VIRTUAL:
this._registerSensorOrMotor(data[3], data[5]) this._registerSensorOrMotor(portID, data[5])
break; break;
case BoostIOEvent.DETACHED: case BoostIOEvent.DETACHED:
this._clearPort(data[3]); this._clearPort(portID);
break; break;
default: default:
console.log("No I/O Event case found!") console.log("No I/O Event case found!")
@ -840,7 +867,7 @@ class Boost {
break; break;
case BoostMessageTypes.PORT_VALUE: case BoostMessageTypes.PORT_VALUE:
//console.log(buf2hex(data)) //console.log(buf2hex(data))
var type = this._ports[data[3]]; var type = this._ports[portID];
//var valueFormat = data.length //var valueFormat = data.length
// TODO: Build a proper value-formatting based on the PORT_INPUT_FORMAT-messages instead of hardcoding value-handling // TODO: Build a proper value-formatting based on the PORT_INPUT_FORMAT-messages instead of hardcoding value-handling
switch(type) { switch(type) {
@ -858,17 +885,26 @@ class Boost {
if (value > 0x7fffffff) { if (value > 0x7fffffff) {
value = value - 0x100000000; value = value - 0x100000000;
} }
//console.log(value); this._motors[portID]._position = value
this._motors[data[3]]._position = value
break; break;
case BoostDevice.CURRENT: case BoostDevice.CURRENT:
case BoostDevice.VOLTAGE: case BoostDevice.VOLTAGE:
break; break;
// Do nothing
default: default:
console.log("Unknown sensor value! Type: " + type) console.log("Unknown sensor value! Type: " + type)
} }
break; break;
case BoostMessageTypes.PORT_OUTPUT_COMMAND_FEEDBACK:
//TODO: Handle messages that contain feedback from more than one port.
var feedback = data[4];
switch(feedback) {
case BoostOutputCommandFeedback.BUFFER_EMPTY_COMMAND_COMPLETED ^ BoostOutputCommandFeedback.IDLE:
this._motors[portID].pendingPromiseFunction();
break;
default:
console.log("Got it but didn't find a motor on: " + portID)
}
break;
case BoostMessageTypes.PORT_INPUT_FORMAT: case BoostMessageTypes.PORT_INPUT_FORMAT:
case BoostMessageTypes.ERROR: case BoostMessageTypes.ERROR:
//DEBUG //DEBUG
@ -884,17 +920,17 @@ class Boost {
* Register a new sensor or motor connected at a port. Store the type of * Register a new sensor or motor connected at a port. Store the type of
* sensor or motor internally, and then register for notifications on input * sensor or motor internally, and then register for notifications on input
* values if it is a sensor. * values if it is a sensor.
* @param {number} connectID - the port to register a sensor or motor on. * @param {number} portID - the port to register a sensor or motor on.
* @param {number} type - the type ID of the sensor or motor * @param {number} type - the type ID of the sensor or motor
* @private * @private
*/ */
_registerSensorOrMotor (connectID, type) { _registerSensorOrMotor (portID, type) {
// Record which port is connected to what type of device // Record which port is connected to what type of device
this._ports[connectID] = type; this._ports[portID] = type;
// Record motor port // Record motor port
if (type === BoostDevice.MOTORINT || type === BoostDevice.MOTOREXT) { if (type === BoostDevice.MOTORINT || type === BoostDevice.MOTOREXT) {
this._motors[connectID] = new BoostMotor(this, connectID); this._motors[portID] = new BoostMotor(this, portID);
} }
// Set input format for tilt or distance sensor // Set input format for tilt or distance sensor
@ -920,7 +956,7 @@ class Boost {
} }
const cmd = this.generateInputCommand( const cmd = this.generateInputCommand(
connectID, portID,
BoostMode[typeString], BoostMode[typeString],
1, 1,
true true
@ -931,19 +967,19 @@ class Boost {
/** /**
* Clear the sensor or motor present at port 1 or 2. * Clear the sensor or motor present at port 1 or 2.
* @param {number} connectID - the port to clear. * @param {number} portID - the port to clear.
* @private * @private
*/ */
_clearPort (connectID) { _clearPort (portID) {
const type = this._ports[connectID]; const type = this._ports[portID];
if (type === BoostDevice.TILT) { if (type === BoostDevice.TILT) {
this._sensors.tiltX = this._sensors.tiltY = 0; this._sensors.tiltX = this._sensors.tiltY = 0;
} }
if (type === BoostDevice.DISTANCE) { if (type === BoostDevice.DISTANCE) {
this._sensors.distance = 0; this._sensors.distance = 0;
} }
this._ports[connectID] = 'none'; this._ports[portID] = 'none';
this._motors[connectID] = null; this._motors[portID] = null;
} }
} }
@ -1173,7 +1209,7 @@ class Scratch3BoostBlocks {
} }
} }
}, },
{ /*{
opcode: 'whenDistance', opcode: 'whenDistance',
text: formatMessage({ text: formatMessage({
id: 'boost.whenDistance', id: 'boost.whenDistance',
@ -1192,7 +1228,7 @@ class Scratch3BoostBlocks {
defaultValue: 50 defaultValue: 50
} }
} }
}, },*/
{ {
opcode: 'whenColor', opcode: 'whenColor',
text: formatMessage({ text: formatMessage({
@ -1226,7 +1262,7 @@ class Scratch3BoostBlocks {
} }
} }
}, },
{ /*{
opcode: 'getDistance', opcode: 'getDistance',
text: formatMessage({ text: formatMessage({
id: 'boost.getDistance', id: 'boost.getDistance',
@ -1250,7 +1286,7 @@ class Scratch3BoostBlocks {
defaultValue: BoostTiltDirection.ANY defaultValue: BoostTiltDirection.ANY
} }
} }
}, },*/
{ {
opcode: 'getTiltAngle', opcode: 'getTiltAngle',
text: formatMessage({ text: formatMessage({
@ -1558,11 +1594,9 @@ class Scratch3BoostBlocks {
const motor = this._peripheral.motor(motorIndex); const motor = this._peripheral.motor(motorIndex);
if (motor) { if (motor) {
motor.turnOnForDegrees(degrees); motor.turnOnForDegrees(degrees);
motor._pendingPromiseFunction = resolve
} }
}); });
// Run for some time even when no motor is connected
setTimeout(resolve, degrees);
}); });
} }
@ -1733,7 +1767,7 @@ class Scratch3BoostBlocks {
* @property {string} OP - the comparison operation: '<' or '>'. * @property {string} OP - the comparison operation: '<' or '>'.
* @property {number} REFERENCE - the value to compare against. * @property {number} REFERENCE - the value to compare against.
* @return {boolean} - the result of the comparison, or false on error. * @return {boolean} - the result of the comparison, or false on error.
*/
whenDistance (args) { whenDistance (args) {
switch (args.OP) { switch (args.OP) {
case '<': case '<':
@ -1745,6 +1779,7 @@ class Scratch3BoostBlocks {
return false; return false;
} }
} }
*/
/** /**
* Test whether the tilt sensor is currently tilted. * Test whether the tilt sensor is currently tilted.
@ -1768,10 +1803,10 @@ class Scratch3BoostBlocks {
/** /**
* @return {number} - the distance sensor's value, scaled to the [0,100] range. * @return {number} - the distance sensor's value, scaled to the [0,100] range.
*/
getDistance () { getDistance () {
return this._peripheral.distance; return this._peripheral.distance;
} }*/
/** /**
* @return {number} - the vision sensor's color value. Indexed LEGO brick colors. * @return {number} - the vision sensor's color value. Indexed LEGO brick colors.