/**
* Thymio exension for ScratchX
* v 1.1 for internal use
* Created by Elisa Bernardoni on July 14, 2017
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
(function(ext) {
var ASEBAHTTPURL = 'http://localhost:3000/';
var VMAX = 500;
var VMIN = -500;
var DEBUG = false;
var LMAX = 32;
var LMIN = 0;
var source = null;
var connected = 0;
var eventCompleteCallback = false;
var cachedValues = Array();
var leds = [0, 0, 0];
var dial = -1;
loadAesl();
connect();
/**
* Cleanup function when the extension is unloaded
*/
ext._shutdown = function() {
if (DEBUG) {
console.log("SHUTDOWN");
}
var args = Array();
sendAction('Q_reset', args, function() {
cachedValues = Array();
disconnect();
});
};
/**
* Reset function mandatory for ScratchX- call Q_reset
*/
ext.resetAll = function() {
if (DEBUG) {
console.log("resetAll");
}
//TODO dial
//TODO Leds
//TODO motors
//leds ?
var args = Array();
sendAction('Q_reset', args, function() {
cachedValues = Array();
});
};
/*
ext.poll = function() {
console.log("poll");
//ScratchX does not call poll
};
*/
/**
* Status reporting code
* Use this to report missing hardware, plugin or unsupported browser
*/
ext._getStatus = function() {
if (connected == 0) return {
status: 0,
msg: 'Thymio disconnected'
};
if (connected == 1) return {
status: 1,
msg: 'Probing for Thymio'
};
return {
status: 2,
msg: 'Ready'
};
};
/**
* The function subscribes to the Thymio’s SSE stream, sets an Event Listener on messages received
* and stores R_state variable in cachedValues
*/
function connect() {
if (source) {
source.close();
source = null;
}
source = new EventSource(ASEBAHTTPURL + 'nodes/thymio-II/events');
source.addEventListener('open', function(e) {
if (DEBUG) {
console.log("open");
}
});
source.addEventListener('message', function(e) {
eventData = e.data.split(" ");
connected = 2;
if (eventData[0] == "R_state_update") {
cachedValues = eventData;
} else {
if (DEBUG) {
console.log("emitted " + eventData)
}
}
// If block requires to check event message for completion, it will set eventCompleteCallback
if (typeof eventCompleteCallback == 'function') {
// We pass eventData to be able to read event message
eventCompleteCallback(eventData);
}
});
source.addEventListener('error', function(e) {
disconnect('Event stream closed');
connected = 0;
connect();
});
}
/**
* The function sends the code of thymio_motion.aesl to asebahttp bridge
*/
function disconnect() {
if (source) {
source.close();
source = null;
}
connected = 0;
}
/**
* TEST - The function sends Aesl for Thtmio to asebahttp bridge
*/
function loadAesl() {
if(DEBUG){
console.log("Send Aesl for Thymio");
}
var xmlstring=' \
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
var tmp[9] \
var Qid[QUEUE] = [ 0,0,0,0 ] \
var Qtime[QUEUE] = [ 0,0,0,0 ] \
var QspL[QUEUE] = [ 0,0,0,0 ] \
var QspR[QUEUE] = [ 0,0,0,0 ] \
var Qpc = 0 \
var Qnx = 0 \
var distance.front = 190 \
var distance.back = 125 \
var angle.front = 0 \
var angle.back = 0 \
var angle.ground = 0 \
var odo.delta \
var odo.theta = 0 \
var odo.x = 0 \
var odo.y = 0 \
var odo.degree \
var R_state.do = 1 \
var R_state[27] \
mic.threshold = 12 \
onevent motor \
odo.delta = (motor.right.target + motor.left.target) / 2 \
call math.muldiv(tmp[0], (motor.right.target - motor.left.target), 3406, 10000) \
odo.theta += tmp[0] \
call math.cos(tmp[0:1],[odo.theta,16384-odo.theta]) \
call math.muldiv(tmp[0:1], [odo.delta,odo.delta],tmp[0:1], [32767,32767]) \
odo.x += tmp[0]/45 \
odo.y += tmp[1]/45 \
odo.degree = 90 - (odo.theta / 182) \
if Qtime[Qpc] > 0 then \
emit Q_motion_started([Qid[Qpc], Qtime[Qpc], QspL[Qpc], QspR[Qpc], Qpc]) \
Qtime[Qpc] = 0 - Qtime[Qpc] \
end \
if Qtime[Qpc] < 0 then \
motor.left.target = QspL[Qpc] \
motor.right.target = QspR[Qpc] \
Qtime[Qpc] += 1 \
if Qtime[Qpc] == 0 then \
emit Q_motion_ended([Qid[Qpc], Qtime[Qpc], QspL[Qpc], QspR[Qpc], Qpc]) \
Qid[Qpc] = 0 \
Qpc = (Qpc+1)%QUEUE \
if Qtime[Qpc] == 0 and Qpc == Qnx then \
emit Q_motion_noneleft([Qpc]) \
motor.left.target = 0 \
motor.right.target = 0 \
end \
end \
end \
if Qtime[Qpc] == 0 and Qpc != Qnx then \
Qpc = (Qpc+1)%QUEUE \
end \
call math.fill(tmp,0) \
tmp[Qnx]=1 \
tmp[Qpc]=4 \
call leds.buttons(tmp[0],tmp[1],tmp[2],tmp[3]) \
sub motion_add \
if (Qnx != Qpc or (Qnx == Qpc and Qtime[Qpc] == 0)) and Qid[0]!=tmp[0] and Qid[1]!=tmp[0] and Qid[2]!=tmp[0] and Qid[3]!=tmp[0] then \
Qid[Qnx] = tmp[0] \
Qtime[Qnx] = tmp[1] \
QspL[Qnx] = tmp[2] \
QspR[Qnx] = tmp[3] \
emit Q_motion_added([Qid[Qnx], Qtime[Qnx], QspL[Qnx], QspR[Qnx], Qnx]) \
Qnx = (Qnx+1)%QUEUE \
end \
sub motion_cancel \
for tmp[1] in 1:QUEUE do \
if Qid[tmp[1]-1] == tmp[0] then \
emit Q_motion_cancelled([Qid[tmp[1]-1], Qtime[tmp[1]-1], QspL[tmp[1]-1], QspR[tmp[1]-1], tmp[1]-1]) \
Qtime[tmp[1]-1] = -1 \
end \
end \
\
onevent Q_add_motion \
tmp[0:3] = event.args[0:3] \
callsub motion_add \
\
onevent Q_cancel_motion \
tmp[0] = event.args[0] \
callsub motion_cancel \
\
onevent Q_set_odometer \
odo.theta = (((event.args[0] + 360) % 360) - 90) * 182 \
odo.x = event.args[1] * 28 \
odo.y = event.args[2] * 28 \
\
onevent Q_reset \
call math.fill(Qid,0) \
call math.fill(Qtime,0) \
call math.fill(QspL,0) \
call math.fill(QspR,0) \
call math.fill(Qpc,0) \
call math.fill(Qnx,0) \
motor.left.target = 0 \
motor.right.target = 0 \
emit Q_motion_noneleft([Qpc]) \
\
onevent buttons \
call math.dot(distance.front, prox.horizontal,[13,26,39,26,13,0,0],11) \
call math.clamp(distance.front,190-distance.front,0,190) \
call math.max(distance.back, prox.horizontal[5],prox.horizontal[6]) \
call math.muldiv(distance.back, distance.back, 267,10000) \
call math.clamp(distance.back,125-distance.back,0,125) \
call math.dot(angle.front, prox.horizontal,[4,3,0,-3,-4,0,0],9) \
call math.dot(angle.back, prox.horizontal,[0,0,0,0,0,-4,4],9) \
call math.dot(angle.ground, prox.ground.delta,[4,-4],7) \
R_state = [ ((((acc[0]/2)+16)%32)<<10) + ((((acc[1]/2)+16)%32)<<5) + (((acc[2]/2)+16)%32), \
(((mic.intensity/mic.threshold)%8)<<8) + \
(0<<5) + \
(button.backward<<4) + \
(button.center<<3) + \
(button.forward<<2) + \
(button.left<<1) + \
button.right, \
((angle.ground+90) << 8) + (angle.back+90), \
angle.front, \
(distance.back<<8) + distance.front, \
motor.left.target, \
motor.right.target, \
motor.left.speed, \
motor.right.speed, \
odo.degree, \
odo.x, \
odo.y, \
prox.comm.rx, \
prox.comm.tx, \
prox.ground.delta[0:1], \
prox.horizontal[0:6], \
Qid[0:3] \
] \
\
onevent prox \
if R_state.do==1 then \
emit R_state_update(R_state) \
end \
\
onevent V_leds_bottom \
if event.args[0]==0 then \
call leds.bottom.left(event.args[1],event.args[2],event.args[3]) \
else \
call leds.bottom.right(event.args[1],event.args[2],event.args[3]) \
end \
\
onevent V_leds_buttons \
call leds.buttons(event.args[0],event.args[1], \
event.args[2],event.args[3]) \
\
onevent V_leds_circle \
call leds.circle(event.args[0],event.args[1],event.args[2], \
event.args[3],event.args[4],event.args[5], \
event.args[6],event.args[7]) \
\
onevent V_leds_prox_h \
call leds.prox.h(event.args[0],event.args[1],event.args[2], \
event.args[3],event.args[4],event.args[5], \
event.args[6],event.args[7]) \
\
onevent V_leds_prox_v \
call leds.prox.v(event.args[0],event.args[1]) \
\
onevent V_leds_rc \
call leds.rc(event.args[0]) \
\
onevent V_leds_sound \
call leds.sound(event.args[0]) \
\
onevent V_leds_temperature \
call leds.temperature(event.args[0],event.args[1]) \
\
onevent V_leds_top \
call leds.top(event.args[0],event.args[1],event.args[2]) \
\
onevent A_sound_system \
call sound.system(event.args[0]) \
\
onevent A_sound_freq \
call sound.freq(event.args[0],event.args[1]) \
\
onevent A_sound_play \
call sound.play(event.args[0]) \
\
onevent A_sound_record \
call sound.record(event.args[0]) \
\
onevent A_sound_replay \
call sound.replay(event.args[0]) \
\
onevent M_motor_left \
motor.left.target = event.args[0] \
\
onevent M_motor_right \
motor.right.target = event.args[0] \
\
\
';
$.ajax({
url: 'http://localhost:3000/nodes/thymio-II',
type: 'PUT',
data: xmlstring,
success: function(data) {
connect();
}
});
}
/**
* The robot moves forward. If the distance is negative, the robot moves back.
* Speed about 100 mm/s.
* @param {distance} number - Distance in mm
* @param {function} callback - ScratchX callback function
*/
ext.scratch_move = function(distance, callback) {
if (DEBUG) {
console.log("call scratch_move " + distance);
}
// Construct args to send with request
var mm = parseInt(distance);
if (mm == 0) {
var args = Array();
args.push(100 * 32 / 10); //speed=10mm/s
sendAction('M_motor_left', args, function() {
sendAction('M_motor_right', args, callback);
});
} else {
var speed;
if (Math.abs(mm) < 20) {
speed = 20;
} else {
if (Math.abs(mm) > 150) {
speed = 150;
} else {
speed = Math.abs(mm);
}
}
var time = Math.abs(mm) * 100 / speed; // time measured in 100 Hz ticks
speed = speed * 32 / 10;
var args = Array();
args.push("Q_add_motion");
args.push(time);
if (mm > 0) {
args.push(speed);
args.push(speed);
} else {
args.push(speed * -1);
args.push(speed * -1);
}
// Send request
requestSend(args, 2,
function(resp) {
if (DEBUG) {
console.log("sent scratch_move " + distance);
}
// Set message to look for in event "message" and execute callback (next block) when received
eventCompleteCallback = function(eventData) {
if (eventData[0].match(/^Q_motion_noneleft/)) {
if (DEBUG) {
console.log(eventData[0] + "=> call callback ");
}
callback();
};
};
});
}
};
/**
* The robot moves forward with a given speed. If the distance is negative, the robot moves back.
* If the value of the distance is not specified, the robot does not stop.
* @param {distance} number - Distance in mm
* @param {speed} number - Speed in mm/s
* @param {function} callback - ScratchX callback function
*/
ext.scratch_move_with_speed = function(distance, speed, callback) {
if (DEBUG) {
console.log("called scratch_move_with_speed " + distance);
}
// Construct args to send with request
var mm = parseInt(distance);
var speed = parseInt(Math.abs(speed));
speed = parseInt(clamp(speed, VMIN * 10 / 32, VMAX * 10 / 32));
if (mm == 0) {
var args = Array();
args.push(speed * 32 / 10); //speed=10mm/s
sendAction('M_motor_left', args, function() {
sendAction('M_motor_right', args, callback);
});
} else {
var time = Math.abs(mm) * 100 / speed; // time measured in 100 Hz ticks
speed = speed * 32 / 10;
var args = Array();
args.push("Q_add_motion");
args.push(time);
if (mm > 0) {
args.push(speed);
args.push(speed);
} else {
args.push(speed * -1);
args.push(speed * -1);
}
// Send request
requestSend(args, 2, function(resp) {
if (DEBUG) {
console.log("sent scratch_move_with_speed " + distance);
}
// Set message to look for in event "message" and execute callback (next block) when received
eventCompleteCallback = function(eventData) {
if (eventData[0].match(/^Q_motion_noneleft/)) {
if (DEBUG) {
console.log(eventData[0] + "=> call callback ");
}
callback();
};
};
});
}
};
/**
* The robot moves forward with a given time. If the distance is negative, the robot moves back.
* @param {distance} number - Distance in mm
* @param {time} number - Time in s
* @param {function} callback - ScratchX callback function
*/
ext.scratch_move_with_time = function(distance, time, callback) {
if (DEBUG) {
console.log("called scratch_move_with_speed " + distance);
}
// Construct args to send with request
var mm = parseInt(distance);
var time = parseInt(Math.abs(time));
var speed = parseInt(Math.abs(mm) / time);
speed = parseInt(clamp(speed, VMIN * 10 / 32, VMAX * 10 / 32));
var time = time * 100; // time measured in 100 Hz ticks
speed = speed * 32 / 10;
var args = Array();
args.push("Q_add_motion");
args.push(time);
if (mm > 0) {
args.push(speed);
args.push(speed);
} else {
args.push(speed * -1);
args.push(speed * -1);
}
// Send request
requestSend(args, 2, function(resp) {
if (DEBUG) {
console.log("sent scratch_move_with_speed " + distance);
}
// Set message to look for in event "message" and execute callback (next block) when received
eventCompleteCallback = function(eventData) {
if (eventData[0].match(/^Q_motion_noneleft/)) {
if (DEBUG) {
console.log(eventData[0] + "=> call callback ");
}
callback();
};
};
});
};
/**
* The robot stops.
*/
ext.scratch_stop = function() {
if (DEBUG) {
console.log("scratch_stop");
}
var args = Array();
args.push(0);
sendAction('M_motor_left', args, function() {
sendAction('M_motor_right', args);
});
};
/**
* The robot moves on an arc of circle of a given radius and of a given angle
* If the radius> 0, it goes forward otherwise goes backwards. If angle> 0 begins to the right, if negative, starts to the left.
* @param {radius} number - Radius in mm
* @param {angle} number - Angle in degrees
* @param {function} callback - ScratchX callback function
*/
ext.scratch_arc = function(radius, angle, callback) {
if (DEBUG) {
console.log("scratch_arc " + radius + " " + angle);
}
angle = parseInt(angle);
radius = parseInt(radius);
if (Math.abs(radius) < 100)
radius = (radius < 0) ? -100 : 100; // although actually, we should just call scratch_turn
var ratio = (Math.abs(radius) - 95) * 10000 / Math.abs(radius);
var time = (angle * (50.36 * radius + 25)) / 3600;
var v_out = 400;
var v_in = v_out * ratio / 10000;
if (radius < 0) {
v_in = -v_in;
v_out = -v_out;
}
var args = Array();
args.push("Q_add_motion");
args.push(time);
args.push((angle > 0) ? v_out : v_in);
args.push((angle > 0) ? v_in : v_out);
// Send request
requestSend(args, 2, function(resp) {
if (DEBUG) {
console.log("sent scratch_arc " + angle + " " + radius);
}
// Set message to look for in event "message" and execute callback (next block) when received
eventCompleteCallback = function(eventData) {
if (eventData[0].match(/^Q_motion_noneleft/)) {
if (DEBUG) {
console.log(eventData[0] + "=> call callback ");
}
callback();
};
};
});
};
/**
* The robots turns in place of a given angle (to the left if> 0, to the right if <0) then stops.
* @param {angle} number - Angle in degrees
* @param {function} callback - ScratchX callback function
*/
ext.scratch_turn = function(angle, callback) {
if (DEBUG) {
console.log("scratch_turn " + angle);
}
angle = parseInt(angle);
var speed, time;
if (Math.abs(angle) > 90) {
speed = 65 * 32 / 10;
time = Math.abs(angle) * 1.3;
} else {
speed = 43 * 32 / 10;
time = Math.abs(angle) * 2.0;
time = angle * angle * 2.0 / (Math.abs(angle) * 1.016 - 0.52); // nonlinear correction
}
var args = Array();
args.push("Q_add_motion");
args.push(time);
args.push((angle > 0) ? speed : speed * -1);
args.push((angle > 0) ? speed * -1 : speed);
// Send request
requestSend(args, 2, function(resp) {
if (DEBUG) {
console.log("sent scratch_turn " + angle);
}
// Set message to look for in event "message" and execute callback (next block) when received
eventCompleteCallback = function(eventData) {
if (eventData[0].match(/^Q_motion_noneleft/)) {
if (DEBUG) {
console.log(eventData[0] + "=> call callback ");
}
callback();
};
};
});
};
/**
* The robots turns in place of a given angle with a given speed (to the left if angle> 0, to the right if angle <0) then stops.
* If the value of the angle is not specified, the robot does not stop.
* @param {angle} number - Angle in degrees
* @param {speed} number - Speed in mm/s
* @param {function} callback - ScratchX callback function
*/
ext.scratch_turn_with_speed = function(angle, speed, callback) {
if (DEBUG) {
console.log("call scratch_turn_with_speed " + angle + " " + speed);
}
// Construct args to send with request
var angle = parseInt(angle) * 0.78;
var speed = parseInt(Math.abs(speed));
speed = parseInt(clamp(speed, VMIN * 10 / 32, VMAX * 10 / 32));
if (angle == 0) {
var args = Array();
args.push(speed * 32 / 10); //speed=10mm/s
sendAction('M_motor_left', args, function() {
args[0] = -args[0];
sendAction('M_motor_right', args, callback);
});
} else {
var time = Math.abs(angle) * 100 / speed; // time measured in 100 Hz ticks
speed = speed * 32 / 10;
var args = Array();
args.push("Q_add_motion");
args.push(time);
if (angle > 0) {
args.push(speed);
args.push(speed * -1);
} else {
args.push(speed * -1);
args.push(speed);
}
// Send request
requestSend(args, 2, function(resp) {
if (DEBUG) {
console.log("sent scratch_turn_with_speed " + angle + " " + speed);
}
// Set message to look for in event "message" and execute callback (next block) when received
eventCompleteCallback = function(eventData) {
if (eventData[0].match(/^Q_motion_noneleft/)) {
if (DEBUG) {
console.log(eventData[0] + "=> call callback ");
}
callback();
};
};
});
}
};
/**
* The robots turns in place of a given angle in a given time (to the left if angle > 0, to the right if angle <0) then stops.
* @param {angle} number - Angle in degrees
* @param {time} number - Time in s
* @param {function} callback - ScratchX callback function
*/
ext.scratch_turn_with_time = function(angle, time, callback) {
if (DEBUG) {
console.log("call scratch_turn_with_time " + angle + " " + time);
}
// Construct args to send with request
var angle = parseInt(angle) * 0.78;
var time = parseInt(Math.abs(time));
var speed = Math.abs(angle) / time; // time measured in 100 Hz ticks
speed = speed * 32 / 10;
var args = Array();
args.push("Q_add_motion");
args.push(time * 100);
if (angle > 0) {
args.push(speed);
args.push(speed * -1);
} else {
args.push(speed * -1);
args.push(speed);
}
// Send request
requestSend(args, 2, function(resp) {
if (DEBUG) {
console.log("call scratch_turn_with_time " + angle + " " + time);
}
// Set message to look for in event "message" and execute callback (next block) when received
eventCompleteCallback = function(eventData) {
if (eventData[0].match(/^Q_motion_noneleft/)) {
if (DEBUG) {
console.log(eventData[0] + "=> call callback ");
}
callback();
};
};
});
};
/**
* Run the left/right/all motors.
* @param {motor} string - Item of menu 'leftrightal'.
* @param {value} value - Speed in Aseba unities.
*/
ext.scratch_motor = function(motor, value) {
value = parseInt(clamp(value, VMIN, VMAX));
if (DEBUG) {
console.log("called scratch_motor " + value);
}
var args = Array();
args.push(value);
if (motor == menus[lang]['leftrightall'][0]) {
sendAction('M_motor_left', args);
} else if (motor == menus[lang]['leftrightall'][1]) {
sendAction('M_motor_right', args);
} else {
sendAction('M_motor_left', args, function() {
sendAction('M_motor_right', args);
});
}
};
/**
* Returns the value returned by a given position sensor.
* @param {sensor} number - 0 for the left, 1 for the right
*/
ext.ground = function(sensor) {
if (DEBUG) {
console.log("called ground " + sensor);
}
sensor = parseInt(sensor);
if (sensor == 0 || sensor == 1)
return parseInt(cachedValues[15 + parseInt(sensor)]);
else return 0;
};
/**
* Returns the value returned by a given position sensor.
* @param {sensor} number - 0 to 6 (front 0 to 4, back 6 or 7)
*/
ext.proximity = function(sensor) {
if (DEBUG) {
console.log("called proximity " + sensor);
}
sensor = parseInt(sensor);
if (sensor >= 0 && sensor <= 6)
return parseInt(cachedValues[17 + sensor]);
else return 0;
};
/**
* Returns the value returned by a given position sensor.
* @param {proxsensor} string - Item of menu 'proxsensors'.
*/
ext.proximity2 = function(proxsensor) {
if (DEBUG) {
console.log("called proximity2 " + proxsensor);
}
var sensor = -1;
if (proxsensor == menus[lang]['proxsensors'][0]) {
sensor = 0;
} else if (proxsensor == menus[lang]['proxsensors'][1]) {
sensor = 1;
} else if (proxsensor == menus[lang]['proxsensors'][2]) {
sensor = 2;
} else if (proxsensor == menus[lang]['proxsensors'][3]) {
sensor = 3;
} else if (proxsensor == menus[lang]['proxsensors'][4]) {
sensor = 4;
} else if (proxsensor == menus[lang]['proxsensors'][5]) {
sensor = 5;
} else if (proxsensor == menus[lang]['proxsensors'][6]) {
sensor = 6;
}
if (sensor >= 0 && sensor <= 6)
return parseInt(cachedValues[17 + sensor]);
else return 0;
};
/**
* Sets the color RGB of a given led.
* @param {led} string - Item of menu 'light'
* @param {r} number - Red value (0 to 32)
* @param {g} number - Green value (0 to 32)
* @param {b} number - Blue value (0 to 32)
*/
ext.scratch_leds = function(led, r, g, b) {
if (DEBUG) {
console.log("call scratch_leds " + led + " " + r + " " + g + " " + b);
}
var args = Array();
args.push(parseInt(clamp(r, LMIN, LMAX)));
args.push(parseInt(clamp(g, LMIN, LMAX)));
args.push(parseInt(clamp(b, LMIN, LMAX)));
if (led == menus[lang]['light'][0]) {
sendAction('V_leds_top', args);
args.unshift(0);
sendAction('V_leds_bottom', args);
args[0] = 1;
sendAction('V_leds_bottom', args);
} else if (led == menus[lang]['light'][1]) {
sendAction('V_leds_top', args);
} else if (led == menus[lang]['light'][2]) {
args.unshift(0);
sendAction('V_leds_bottom', args);
args[0] = 1;
sendAction('V_leds_bottom', args);
} else if (led == menus[lang]['light'][3]) {
args.unshift(0);
sendAction('V_leds_bottom', args);
} else if (led == menus[lang]['light'][4]) {
args.unshift(1);
sendAction('V_leds_bottom', args);
}
};
/**
* Adds the value to the current color for a given led.
* Color coding Scratch (0 to 200) 0 red → chroma circle mod 198
* @param {color} number - Value to add.
* @param {led} string - Item of menu 'light'
*/
ext.scratch_change_leds = function(color, led) {
if (DEBUG) {
console.log("call scratch_change_leds " + color + " " + led);
}
var mask;
if (led == menus[lang]['light'][0]) //ALL
mask = 7;
else if (led == menus[lang]['light'][1]) //TOP
mask = 1;
else if (led == menus[lang]['light'][2]) //BOTTOM
mask = 6;
else if (led == menus[lang]['light'][3]) //BOTTOM Left
mask = 2;
else if (led == menus[lang]['light'][4]) //BOTTOM Right
mask = 4;
else
mask = 7;
if (mask == 1) {
var rgb = makeLedsRGBVector((parseInt(color + leds[0]) % 198));
sendAction('V_leds_top', rgb, function() {
leds[0] = color + leds[0];
});
} else if (mask == 2) {
var rgb = makeLedsRGBVector((parseInt(color + leds[1]) % 198));
rgb.unshift(0);
sendAction('V_leds_bottom', rgb, function() {
leds[1] = color + leds[1];
});
} else if (mask == 4) {
var rgb = makeLedsRGBVector((parseInt(color + leds[2]) % 198));
rgb.unshift(1);
sendAction('V_leds_bottom', rgb, function() {
leds[2] = color + leds[2];
});
} else if (mask == 6) {
var rgb = makeLedsRGBVector((parseInt(color + leds[1]) % 198));
rgb.unshift(0);
sendAction('V_leds_bottom', rgb, function() {
leds[1] = color + leds[1];
rgb = makeLedsRGBVector((parseInt(color + leds[2]) % 198));
rgb.unshift(1);
sendAction('V_leds_bottom', rgb, function() {
leds[2] = color + leds[2];
});
});
} else {
var rgb = makeLedsRGBVector((parseInt(color + leds[0]) % 198));
sendAction('V_leds_top', rgb, function() {
leds[0] = color + leds[0];
rgb = makeLedsRGBVector((parseInt(color + leds[1]) % 198));
rgb.unshift(0);
sendAction('V_leds_bottom', rgb, function() {
leds[1] = color + leds[1];
rgb = makeLedsRGBVector((parseInt(color + leds[2]) % 198));
rgb.unshift(1);
sendAction('V_leds_bottom', rgb, function() {
leds[2] = color + leds[2];
});
});
});
}
};
/**
* Sets the color for a given led.
* Color coding Scratch (0 to 200) 0 red → chroma circle mod 198
* @param {color} number - Value to add.
* @param {led} string - Item of menu 'light'
*/
ext.scratch_set_leds = function(color, led) {
if (DEBUG) {
console.log("call scratch_set_leds " + color + " " + led);
}
color = parseInt(color) % 198;
var mask;
if (led == menus[lang]['light'][0]) //ALL
mask = 7;
else if (led == menus[lang]['light'][1]) //TOP
mask = 1;
else if (led == menus[lang]['light'][2]) //BOTTOM
mask = 6;
else if (led == menus[lang]['light'][3]) //BOTTOM Left
mask = 2;
else if (led == menus[lang]['light'][4]) //BOTTOM Right
mask = 4;
else
mask = 7;
var rgb = makeLedsRGBVector(color); // by default, "V_leds_top"
if (mask == 1) {
sendAction('V_leds_top', rgb, function() {
leds[0] = color;
});
} else if (mask == 2) {
rgb.unshift(0);
sendAction('V_leds_bottom', rgb, function() {
leds[1] = color;
});
} else if (mask == 4) {
rgb.unshift(1);
sendAction('V_leds_bottom', rgb, function() {
leds[2] = color;
});
} else if (mask == 6) {
rgb.unshift(0);
sendAction('V_leds_bottom', rgb, function() {
leds[1] = color;
rgb[0] = 1;
sendAction('V_leds_bottom', rgb, function() {
leds[2] = color;
});
});
} else {
sendAction('V_leds_top', rgb, function() {
leds[0] = color;
rgb.unshift(0);
sendAction('V_leds_bottom', rgb, function() {
leds[1] = color;
rgb[0] = 1;
sendAction('V_leds_bottom', rgb, function() {
leds[2] = color;
});
});
});
}
};
/**
* Returns the temperature in °C
*/
ext.temperature = function(callback) {
if (DEBUG) {
console.log("called temperature");
}
var args = Array();
sendAction('temperature', args, function(data) {
callback(parseInt(data[0]) / 10);
});
};
/**
* Returns the color for a given led.
* @param {led} string - Item of menu 'singlelight'
*/
ext.leds = function(led) {
if (DEBUG) {
console.log("called leds " + led);
}
if (led == menus[lang]['singlelight'][0]) {
return leds[0];
} else if (led == menus[lang]['singlelight'][1]) {
return leds[1];
} else if (led == menus[lang]['singlelight'][2]) {
return leds[2];
}
};
/**
* Lights the circle LEDs
* @param {l0} number - 0 to 32
* @param {l1} number - 0 to 32
* @param {l2} number - 0 to 32
* @param {l3} number - 0 to 32
* @param {l4} number - 0 to 32
* @param {l5} number - 0 to 32
* @param {l6} number - 0 to 32
* @param {l7} number - 0 to 32
*/
ext.V_leds_circle = function(l0, l1, l2, l3, l4, l5, l6, l7) {
if (DEBUG) {
console.log("called V_leds_circle " + l0 + " " + l1 + " " + l2 + " " + l3 + " " + l4 + " " + l5 + " " + l6 + " " + l7);
}
var args = Array();
args.push(parseInt(clamp(l0, LMIN, LMAX)));
args.push(parseInt(clamp(l1, LMIN, LMAX)));
args.push(parseInt(clamp(l2, LMIN, LMAX)));
args.push(parseInt(clamp(l3, LMIN, LMAX)));
args.push(parseInt(clamp(l4, LMIN, LMAX)));
args.push(parseInt(clamp(l5, LMIN, LMAX)));
args.push(parseInt(clamp(l6, LMIN, LMAX)));
args.push(parseInt(clamp(l7, LMIN, LMAX)));
sendAction('V_leds_circle', args);
};
/**
* Lights the next circle LED
* @param {menu} number - Item of 'leftright' menu
*/
ext.scratch_next_dial = function(menu) {
if (DEBUG) {
console.log("called scratch_next_dial " + menu);
}
if (dial == -1) {
dial = 0;
} else {
if (menu == menus[lang]['leftright'][0]) {
dial = (dial + 1) % 8;
} else {
dial = (8 + (dial - 1)) % 8;
}
}
var args = Array();
for (i = 0; i < 8; i++)
args.push(0);
args[dial] = 32;
sendAction('V_leds_circle', args);
};
/**
* Sets the value of prox.comm.tx
* @param {value} number - prox.comm.tx value to set
*/
ext.emit = function(value) {
if (DEBUG) {
console.log("called emit " + value);
}
value = parseInt(value)
var args = Array();
args.push(value);
sendAction('prox.comm.tx', args);
};
/**
* Lights the button LEDs
* @param {forward} number - Forward LED value (0 to 32)
* @param {right} number - Right LED value (0 to 32)
* @param {backward} number - Backward LED value (0 to 32)
* @param {left} number - Left LED value (0 to 32)
*/
ext.V_leds_buttons = function(forward, right, backward, left) {
if (DEBUG) {
console.log("called V_leds_buttons " + forward + " " + right + " " + backward + " " + left);
}
var args = Array();
args.push(parseInt(clamp(forward, LMIN, LMAX)));
args.push(parseInt(clamp(right, LMIN, LMAX)));
args.push(parseInt(clamp(backward, LMIN, LMAX)));
args.push(parseInt(clamp(left, LMIN, LMAX)));
sendAction('V_leds_buttons', args);
};
/**
* Lights the temperature sensor LEDs
* @param {hot} number - Red LED value (0 to 32)
* @param {cold} number - Blue LED value (0 to 32)
*/
ext.V_leds_temperature = function(hot, cold) {
if (DEBUG) {
console.log("called V_leds_temperature " + hot + " " + cold);
}
var args = Array();
args.push(parseInt(clamp(hot, LMIN, LMAX)));
args.push(parseInt(clamp(cold, LMIN, LMAX)));
sendAction('V_leds_temperature', args);
};
/**
* Lights the sound sensor LED
* @param {value} number - sound LED value (0 to 32)
*/
ext.V_leds_sound = function(value) {
if (DEBUG) {
console.log("called V_leds_sound " + value);
}
var args = Array();
args.push(parseInt(clamp(value, LMIN, LMAX)));
sendAction('V_leds_sound', args);
};
/**
* Light the RC sensor LED
* @param {value} number - RC LED value (0 to 32)
*/
ext.V_leds_rc = function(value) {
if (DEBUG) {
console.log("called V_leds_rc " + value);
}
var args = Array();
args.push(parseInt(clamp(value, LMIN, LMAX)));
sendAction('V_leds_rc', args);
};
/**
* Lights the ground sensor LEDs
* @param {fl} number - front left value (0 to 32)
* @param {flm} number - front left value (0 to 32)
* @param {flc} number - front left value (0 to 32)
* @param {frc} number - front left value (0 to 32)
* @param {frm} number - front left value (0 to 32)
* @param {fr} number - front left value (0 to 32)
* @param {br} number - front left value (0 to 32)
* @param {bl} number - front left value (0 to 32)
*/
ext.V_leds_prox_h = function(fl, flm,flc,frc,frm,fr,br,bl) {
if (DEBUG) {
console.log("called V_leds_prox_h " + fl+" "+flm+" "+flc+" "+frc+" "+frm+" "+fr+" "+br+" "+bl);
}
var args = Array();
args.push(parseInt(clamp(fl, LMIN, LMAX)));
args.push(parseInt(clamp(flm, LMIN, LMAX)));
args.push(parseInt(clamp(flc, LMIN, LMAX)));
args.push(parseInt(clamp(frc, LMIN, LMAX)));
args.push(parseInt(clamp(frm, LMIN, LMAX)));
args.push(parseInt(clamp(fr, LMIN, LMAX)));
args.push(parseInt(clamp(br, LMIN, LMAX)));
args.push(parseInt(clamp(bl, LMIN, LMAX)));
sendAction('V_leds_prox_h', args);
};
/**
* Lights the ground sensor LEDs
* @param {left} number - left ground sensor LED value (0 to 32)
* @param {right} number - right ground sensor LED value (0 to 32)
*/
ext.V_leds_prox_v = function(left, right) {
if (DEBUG) {
console.log("called V_leds_prox_v " + left + " " + right);
}
var args = Array();
args.push(parseInt(clamp(left, LMIN, LMAX)));
args.push(parseInt(clamp(right, LMIN, LMAX)));
sendAction('V_leds_prox_v', args);
};
/**
* Set to 0 the intensity of top, bottom and circle LEDs
*/
ext.scratch_clear_leds = function() {
if (DEBUG) {
console.log("called scratch_clear_leds");
}
var args = Array();
for (i = 0; i < 8; i++) {
args.push(0);
}
sendAction('V_leds_circle', args, function() {
var args = Array();
for (i = 0; i < 3; i++) {
args.push(0);
}
sendAction('V_leds_top', args, function() {
var args = Array();
for (i = 0; i < 4; i++) {
args.push(0);
}
sendAction('V_leds_bottom', args, function() {
var args = Array();
args.push(1);
for (i = 0; i < 3; i++) {
args.push(0);
}
sendAction('V_leds_bottom', args, function() {
});
});
});
});
};
/**
* Returns a string of the values of the 2 lower sensors.
*/
ext.prox_ground_delta = function() {
if (DEBUG) {
console.log("prox_ground_delta");
}
return cachedValues[15] + " " + cachedValues[16];
};
/**
* Returns a string of the values of the 7 proximity sensors.
*/
ext.prox_horizontal = function() {
if (DEBUG) {
console.log("prox_horizontal");
}
var value = cachedValues[17];
for (i = 1; i < 7; i++) {
value = value + " " + cachedValues[(17 + i)];
}
return value;
};
/**
* Distance from an obstacle calculated from the given sensors (front, back, ground)
* @param {sensor} string - Item of 'sensors' menu.
*/
ext.distance = function(sensor) {
if (DEBUG) {
console.log("distance " + sensor);
}
var num = parseInt(cachedValues[5]);
if (sensor == menus[lang]['sensors'][0]) {
var front = num & 0xff;
return clamp(front, 0, 190);
} else if (sensor == menus[lang]['sensors'][1]) {
var back = ((num >> 8) & 0xff);
return clamp(back, 0, 125);
} else {
var ground = parseInt(cachedValues[15]) + parseInt(cachedValues[16]);
if (ground > 1000)
return 0;
else return 500;
}
};
/**
* Angle under which an obstacle is seen from the robot, calculated from the horizontal sensors of an obstacle.
* @param {sensor} string - Item of 'angles' menu.
*/
ext.angle = function(sensor) {
if (DEBUG) {
console.log("angle " + sensor);
}
if (sensor == menus[lang]['sensors'][0]) {
return parseInt(cachedValues[4]);
} else {
var num = parseInt(cachedValues[3]);
var back = (num % 256) - 90;
var ground = ((num >> 8) % 256) - 90;
if (sensor == menus[lang]['sensors'][1])
return back;
else
return ground;
}
};
/**
* Plays a note (Hz) for a time (s)
* @param {freq} number - freg in Hz
* @param {duration} number - duration in seconds
*/
ext.A_sound_freq = function(freq, duration) {
if (DEBUG) {
console.log("A_sound_freq " + freq + " " + duration);
}
freq = parseInt(freq);
duration = parseInt(parseFloat(duration) * 60);
var args = Array();
args.push(parseInt(freq));
args.push(parseInt(duration));
sendAction('A_sound_freq', args);
};
/**
* Plays a system sound
* @param {sound} number - system sound number
*/
ext.A_sound_system = function(sound) {
if (DEBUG) {
console.log("A_sound_system " + sound);
}
sound = parseInt(sound);
var args = Array();
args.push(parseInt(sound));
sendAction('A_sound_system', args);
};
/**
* Sets the odometer
* @param {theta} number - angle theta in degrees
* @param {x} number - x coordinate
* @param {y} number - y coordinate
*/
ext.Q_set_odometer = function(theta, x, y) {
if (DEBUG) {
console.log("Q_set_odometer " + theta + " " + x + " " + y);
}
var args = Array();
args.push(parseInt(theta));
args.push(parseInt(x));
args.push(parseInt(y));
sendAction('Q_set_odometer', args);
};
/**
* Returns a odometer value
* @param {menu} string - Item of 'odo' menu
*/
ext.odo = function(menu) {
if (DEBUG) {
console.log("odo " + menu);
}
if (menu == menus[lang]['odo'][0]) {
return parseInt(cachedValues[10]);
} else if (menu == menus[lang]['odo'][1]) {
return parseInt(cachedValues[11] / 28);
} else if (menu == menus[lang]['odo'][2]) {
return parseInt(cachedValues[12] / 28);
}
};
ext.button = function(menu) {
if (DEBUG) {
console.log("button "+menu);
}
var num = parseInt(cachedValues[2]);
if (menu == menus[lang]['buttons'][0]) {
var center = parseInt((num >> 3) & 1);
if(center==1) return true;
else return false;
} else if (menu == menus[lang]['buttons'][1]) {
var forward = parseInt((num >> 2) & 1);
if(forward==1) return true;
else return false;
} else if (menu == menus[lang]['buttons'][2]) {
var backward = parseInt((num >> 4) & 1);
if(backward==1) return true;
else return false;
} else if (menu == menus[lang]['buttons'][3]) {
var left = parseInt((num >> 1) & 1);
if(left==1) return true;
else return false;
} else if (menu == menus[lang]['buttons'][4]) {
var right = parseInt((num ) & 1);
if(right==1) return true;
else return false;
}
return false;
};
/**
* Returns the value of the microphone intensity
*/
ext.mic_intensity = function() {
if (DEBUG) {
console.log("mic_intensity ");
}
var num = parseInt(cachedValues[2]);
var intensity = parseInt(((num >> 8) % 8));
return intensity;
};
/**
* Returns the value of prox.comm.rx
*/
ext.receive = function() {
if (DEBUG) {
console.log("called receive");
}
var num = parseInt(cachedValues[13]);
return num;
};
/**
* Returns true if a sound is detected
*/
ext.sound_detected = function() {
if (DEBUG) {
console.log("sound_detected ");
}
var num = parseInt(cachedValues[2]);
var intensity = parseInt(((num >> 8) % 8));
if (intensity > 2) return true;
else return false;
};
/* ext.motor_target = function(menu) {
if (DEBUG) {
console.log("motor_target ");
}
if (menu == menus[lang]['leftright'][0])
return parseInt(cachedValues[6] / 32 * 10);
else if (menu == menus[lang]['leftright'][1])
return parseInt(cachedValues[7] / 32 * 10);
};
ext.motor_speed = function(menu) {
if (DEBUG) {
console.log("motor_speed " + menu);
}
if (menu == menus[lang]['leftright'][0])
return parseInt(cachedValues[8] / 32 * 10);
else if (menu == menus[lang]['leftright'][1])
return parseInt(cachedValues[9] / 32 * 10);
};
*/
/**
* Returns the value of motor (left or right) in Adeba unities.
* @param {menu} string - Item of 'leftright' menu
*/
ext.motor = function(menu) {
if (DEBUG) {
console.log("motor " + menu);
}
if (menu == menus[lang]['leftright'][0])
return parseInt(cachedValues[8]);
else if (menu == menus[lang]['leftright'][1])
return parseInt(cachedValues[9]);
};
/**
* Returns the accelerometer value along a given axis (left-right, top-to-bottom, front-to-back).
* @param {menu} string - Item of 'tilts' menu
*/
ext.tilt = function(menu) {
if (DEBUG) {
console.log("tilt " + menu);
}
var num = cachedValues[1];
if (menu == menus[lang]['tilts'][2]) {
return (((num >> 10) % 32) - 16) * 2;
} else if (menu == menus[lang]['tilts'][0]) {
return (((num >> 5) % 32) - 16) * 2;
} else if (menu == menus[lang]['tilts'][1]) {
return ((num % 32) - 16) * 2;
} else {
return 0;
}
};
/**
* Returns true if the average value of the three axes of the accelerometer is more than the given value
* @param {value} number - threshold
*/
ext.bump = function(value) {
if (DEBUG) {
console.log("bump ");
}
value = parseInt(value);
var num = cachedValues[1];
var acc0 = (((num >> 10) % 32) - 16) * 2
var acc1 = (((num >> 5) % 32) - 16) * 2;
var acc2 = ((num % 32) - 16) * 2
var ave = (acc0 + acc1 + acc2) / 3;
if (parseInt(ave) > value) {
return true;
} else {
return false;
}
};
/**
* Plays a sound from the SD
* @param {sound} number - slot SD sound number
*/
ext.A_sound_play_sd = function(sound) {
if (DEBUG) {
console.log("A_sound_play_sd " + sound);
}
var args = Array();
args.push(parseInt(sound));
sendAction('A_sound_play', args);
};
/**
* Starts or stops recording
* @param {sound} number - recorded sound number
*/
ext.A_sound_record = function(sound) {
if (DEBUG) {
console.log("A_sound_record " + sound);
}
var args = Array();
args.push(parseInt(sound));
sendAction('A_sound_record', args);
};
/**
* Plays a recorded sound
* @param {sound} number - recorded sound number
*/
ext.A_sound_replay = function(sound) {
if (DEBUG) {
console.log("A_sound_replay " + sound);
}
var args = Array();
args.push(parseInt(sound));
sendAction('A_sound_replay', args);
};
/**
* Returns true if an object is detected by a given sensor.
* @param {sound} number - Item of 'sensors' menu.
*/
ext.touching = function(sensor) {
if (DEBUG) {
console.log("touching " + sensor);
}
if (sensor == menus[lang]['sensors'][0]) { //front
var value = 0;
for (i = 0; i < 5; i++) {
value = value + parseInt(cachedValues[17 + i]);
}
if (value / 1000 > 0) {
return true;
} else {
return false;
}
} else if (sensor == menus[lang]['sensors'][1]) { //back
var value = parseInt(cachedValues[22]) + parseInt(cachedValues[23]);
if (value / 1000 > 0) {
return true;
} else {
return false;
}
} else { //ground
var value = parseInt(cachedValues[15]) + parseInt(cachedValues[16]);
if (value / 500 > 0) {
return true;
} else {
return false;
}
}
};
/**
* Returns true if a given proximity sensor is greater than a threshold
* @param {sensor} number - Item of 'sensors' menu.
* @param {threshold} number - threshold
*/
ext.touching_threshold = function(sensor, threshold) {
if (DEBUG) {
console.log("touching " + sensor);
}
threshold = parseInt(threshold);
if (sensor == menus[lang]['sensors'][0]) { //front
for (i = 0; i < 5; i++) {
if (parseInt(cachedValues[17 + i])>threshold) return true;
}
return false;
} else if (sensor == menus[lang]['sensors'][1]) { //back
if (parseInt(cachedValues[22]) > threshold || parseInt(cachedValues[22]) > threshold) {
return true;
}
return false;
} else { //ground
if (parseInt(cachedValues[15]) > threshold || parseInt(cachedValues[16]) > threshold) {
return true;
}
return false;
}
};
// Check for GET param 'lang'
var paramString = window.location.search.replace(/^\?|\/$/g, '');
var vars = paramString.split("&");
var lang = 'fr';
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
if (pair.length > 1 && pair[0] == 'lang')
lang = pair[1];
}
var blocks = {
en: [
["w", "move %n", "scratch_move", 50],
["w", "move %n with speed %n", "scratch_move_with_speed", 50, 50],
["w", "move %n in %n s", "scratch_move_with_time", 50, 1],
["w", "turn %n", "scratch_turn", 45],
["w", "turn %n with speed %n", "scratch_turn_with_speed", 90,50],
["w", "turn %n in %n s", "scratch_turn_with_time", 90,1],
["w", "circle radius %n angle %n", "scratch_arc", 150, 45],
[" ", "stop motors", "scratch_stop"],
[" ", "leds set color %n on %m.light", "scratch_set_leds", 0, "all"],
[" ", "leds change color %n on %m.light", "scratch_change_leds", 0, "all"],
[" ", "leds RGB %m.light %n %n %n", "scratch_leds", "all", 0, 0, 32],
[" ", "leds clear", "scratch_clear_leds"],
[" ", "play system sound %m.sounds", "A_sound_system", 1],
[" ", "play note %n during %n s", "A_sound_freq", 440, 1],
["b", "touching %m.sensors", "touching", "front"],
["b", "touching %m.sensors %n", "touching_threshold", "front", 32],
["r", "proximity sensor %n", "proximity", 2],
["r", "proximity sensor %m.proxsensors", "proximity2", "front far left"],
["r", "ground sensor %n", "ground", 0],
["b", "tap", "bump"],
["r", "tilt on %m.tilts", "tilt", "front-back"],
["R", "temperature", "temperature"],
["b", "sound detected", "sound_detected"],
["r", "sound level", "mic_intensity"],
["h", "button %m.buttons", "button", "center"]
/*,
[" ", "motor %m.leftrightall %n", "scratch_motor", "left", 50],
[" ", "leds next dial %m.leftright", "scratch_next_dial", "left"],
[" ", "leds dial all %n %n %n %n %n %n %n %n", "V_leds_circle", 0, 8, 16, 32, 0, 8, 16, 32],
[" ", "play sound SD %n", "A_sound_play_sd", ""],
[" ", "record sound %n", "A_sound_record", ""],
[" ", "replay sound %n", "A_sound_replay", ""],
[" ", "odometer %n %n %n", "Q_set_odometer", 90, 0, 0],
[" ", "leds sensors h %n %n %n %n %n %n %n %n", "V_leds_prox_h", 0, 16, 32, 32, 16, 0, 32, 32],
[" ", "leds sensors v %n %n", "V_leds_prox_v", 32, 32],
[" ", "leds buttons %n %n %n %n", "V_leds_buttons", 16, 32, 16, 32],
[" ", "leds temperature %n %n", "V_leds_temperature", 32, 8],
[" ", "leds sound %n", "V_leds_sound", 32],
[" ", "leds rc %n", "V_leds_rc", 16],
[" ", "emit %n", "emit", 1],
["r", "receive", "receive"],
["R", "temperature", "temperature"],
["r", "measure motor %m.leftright", "motor", "left"],
/* ["r", "motor %m.leftright speed", "motor_speed", "left" ],
["r", "motor %m.leftright target", "motor_target", "left" ],*/
/*
["r", "distance %m.sensors", "distance", "front"],
["r", "angle %m.angles", "angle", "front"],
["r", "proximity sensors", "prox_horizontal"],
["r", "ground sensors", "prox_ground_delta"],
["r", "leds color %m.light", "leds", "top"],
["r", "odometer %m.odo", "odo", "direction"],
["b", "object detected %m.sensors", "touching", "front"],
["b", "object detected %m.sensors %n", "touching_threshold", "front"],
*/
],
fr: [
["w", "avancer %n", "scratch_move", 50],
["w", "avancer %n avec vitesse %n", "scratch_move_with_speed", 50, 50],
["w", "avancer %n en %n s", "scratch_move_with_time", 50, 1],
["w", "tourner %n", "scratch_turn", 45],
["w", "tourner %n avec vitesse %n", "scratch_turn_with_speed", 90,50],
["w", "tourner %n en %n s", "scratch_turn_with_time", 90,1],
["w", "cercle rayon %n angle %n", "scratch_arc", 150, 45],
[" ", "stop moteurs", "scratch_stop"],
[" ", "leds fixer couleur %n on %m.light", "scratch_set_leds", 0, "tout"],
[" ", "leds changer couleur %n pour %m.light", "scratch_change_leds", 0, "all"],
[" ", "leds RVB %m.light %n %n %n", "scratch_leds", "tout", 0, 0, 32],
[" ", "éteindre leds", "scratch_clear_leds"],
[" ", "jouer son système %m.sounds", "A_sound_system", 1],
[" ", "jouer note %n pendant %n s", "A_sound_freq", 440, 1],
["b", "objet détecté %m.sensors", "touching", "devant"],
["b", "objet détecté %m.sensors %n", "touching_threshold", "devant", 32],
["r", "capteur horizontal %n", "proximity", 2],
["r", "capteur horizontal %m.proxsensors", "proximity2", "devant extrême gauche"],
["r", "capteur dessous %n", "ground", 0],
["b", "choc", "bump"],
["r", "inclinaison %m.tilts", "tilt", "devant-derrière"],
["R", "température", "temperature"],
["b", "bruit", "sound_detected"],
["r", "niveau sonore", "mic_intensity"],
["h", "bouton %m.buttons", "button","central"]
/*,
[" ", "moteur %m.leftrightall %n", "scratch_motor", "gauche", 50],
[" ", "led cadran suivante %m.leftright", "scratch_next_dial", "gauche"],
[" ", "leds cadran toutes %n %n %n %n %n %n %n %n", "V_leds_circle", 0, 8, 16, 32, 0, 8, 16, 32],
[" ", "jouer son SD %n", "A_sound_play_sd", ""],
[" ", "enregistrer son %n", "A_sound_record", ""],
[" ", "rejouer son %n", "A_sound_replay", ""],
[" ", "odomètre %n %n %n", "Q_set_odometer", 90, 0, 0],
[" ", "leds capteurs horiz. %n %n %n %n %n %n %n %n", "V_leds_prox_h", 0, 16, 32, 32, 16, 0, 32, 32],
[" ", "leds capteurs dessous %n %n", "V_leds_prox_v", 32, 32],
[" ", "leds boutons %n %n %n %n", "V_leds_buttons", 16, 32, 16, 32],
[" ", "leds temperature %n %n", "V_leds_temperature", 32, 8],
[" ", "leds sound %n", "V_leds_sound", 32],
[" ", "leds rc %n", "V_leds_rc", 16],
[" ", "emission %n", "emit", 1],
["r", "reception", "receive"],
["r", "mesure moteur %m.leftright", "motor", "gauche"],
/*["r", "moteur %m.leftright vitesse", "motor_speed", "gauche" ],
["r", "moteur %m.leftright target", "motor_target", "gauche" ],*/
/*
["r", "distance %m.sensors", "distance", "devant"],
["r", "angle %m.angles", "angle", "devant"],
["r", "capteurs horizontaux", "prox_horizontal"],
["r", "capteurs dessous", "prox_ground_delta"],
["r", "leds couleur %m.singlelight", "leds", "dessus"],
["r", "odomètre %m.odo", "odo", "direction"],
*/
],
it: [
["w", "avanza di %n", "scratch_move", 50],
["w", "avanza di %n con velocità %n", "scratch_move_with_speed", 50, 50],
["w", "avanza di %n in %n s", "scratch_move_with_time", 50, 1],
["w", "ruota di %n gradi", "scratch_turn", 45],
["w", "ruota di %n gradi con velocità %n", "scratch_turn_with_speed", 90,50],
["w", "ruota di %n gradi in %n s", "scratch_turn_with_time", 90,1],
["w", "fai un cerchio di raggio %n per %n gradi", "scratch_arc", 150, 45],
[" ", "ferma motori", "scratch_stop"],
[" ", "colora LED %n %m.light", "scratch_set_leds", 0, "tutti"],
[" ", "cambia colore LED %n %m.light", "scratch_change_leds", 0, "all"],
[" ", "tutti i LED RVB %m.light %n %n %n", "scratch_leds", "tutti", 0, 0, 32],
[" ", "spegni LED", "scratch_clear_leds"],
[" ", "suona suono Thymio %m.sounds", "A_sound_system", 1],
[" ", "suona nota %n per %n s", "A_sound_freq", 440, 1],
["b", "oggetto rilevato %m.sensors", "touching", "davanti"],
["b", "oggetto rilevato %m.sensors %n", "touching_threshold", "davanti", 32],
["r", "sensore prox. %n", "proximity", 2],
["r", "sensore prox. %m.proxsensors", "proximity2", "tutto a sinistra"],
["r", "sensore terreno %n", "ground", 0],
["b", "urto", "bump"],
["r", "inclinazione %m.tilts", "tilt", "davanti-dietro"],
["R", "temperatura", "temperature"],
["b", "rumore captato", "sound_detected"],
["r", "livello sonoro", "mic_intensity"],
["h", "bottone %m.buttons", "button","centrale"]
/*,
[" ", "motori %m.leftrightall %n", "scratch_motor", "gauche", 50],
[" ", "on LED quadrante %m.leftright", "scratch_next_dial", "sinistro"],
[" ", "on LED quadrante %n %n %n %n %n %n %n %n", "V_leds_circle", 0, 8, 16, 32, 0, 8, 16, 32],
[" ", "suona suono su scheda SD %n", "A_sound_play_sd", ""],
[" ", "registra suono %n", "A_sound_record", ""],
[" ", "riproduci suono %n", "A_sound_replay", ""],
[" ", "inizializza isometria %n %n %n", "Q_set_odometer", 90, 0, 0],
[" ", "LED sensori prox. %n %n %n %n %n %n %n %n", "V_leds_prox_h", 0, 16, 32, 32, 16, 0, 32, 32],
[" ", "LED sensori terreno %n %n", "V_leds_prox_v", 32, 32],
[" ", "LED bottoni %n %n %n %n", "V_leds_buttons", 16, 32, 16, 32],
[" ", "LED bottoni %n %n", "V_leds_temperature", 32, 8],
[" ", "LED microfono %n", "V_leds_sound", 32],
[" ", "LED RC %n", "V_leds_rc", 16],
[" ", "LED RC %n", "emit", 1],
["r", "ricezione", "receive"],
["r", "misura motori %m.leftright", "motor", "sinistro"],
/*["r", "moteur %m.leftright vitesse", "motor_speed", "gauche" ],
["r", "moteur %m.leftright target", "motor_target", "gauche" ],*/
/*
["r", "distanza %m.sensors", "distance", "davanti"],
["r", "angolo %m.angles", "angle", "davanti"],
["r", "sensori di prox.", "prox_horizontal"],
["r", "sensori di prox.", "prox_ground_delta"],
["r", "colore LED %m.singlelight", "leds", "sopra"],
["r", "isometria %m.odo", "odo", "direzione"],*/
],
de: [
["w", "fahre %n mm", "scratch_move", 50],
["w", "fahre %n mm mit Geschwindigkeit %n mm/s", "scratch_move_with_speed", 50, 50],
["w", "fahre %n mm in %n s", "scratch_move_with_time", 50, 1],
["w", "drehe um %n °", "scratch_turn", 45],
["w", "drehe um %n ° mit Geschwindigkeit %n mm/s", "scratch_turn_with_speed", 90, 50],
["w", "drehe um %n in %n s", "scratch_turn_with_time", 90, 1],
["w", "mache einen Bogen mit Radius %n mm und Winkel %n °", "scratch_arc", 150, 45],
[" ", "stoppe die Motoren", "scratch_stop"],
[" ", "setze Lampen %m.light auf Farbe %n", "scratch_set_leds", "alle", 0],
[" ", "wechsle Farbe der Lampen %m.light auf %n", "scratch_change_leds", "alle", 0],
[" ", "setze Lampen %m.light auf Farbe RGB %n %n %n", "scratch_leds", "alle", 0, 0, 32],
[" ", "lösche alle Lampen", "scratch_clear_leds"],
[" ", "spiele Geräusch %m.sounds", "A_sound_system", 1],
[" ", "spiele Ton %n während %n s", "A_sound_freq", 440, 1],
["b", "es hat ein Hindernis %m.sensors", "touching", "vorne"],
["b", "es hat ein Hindernis %m.sensors bei %n", "touching_threshold", "vorne", 32],
["r", "Wert vom Sensor %n", "proximity", 2],
["r", "Wert vom Sensor %m.proxsensors", "proximity2", "vorne ganz links"],
["r", "Wert vom Sensor %n unten", "ground", 0],
["b", "Schock", "bump"],
["r", "geneigt von %m.tilts", "tilt", "vorne nach hinten"],
["R", "Temperatur", "temperature"],
["b", "Geräusch detektiert", "sound_detected"],
["r", "Wert vom Mikrofon", "mic_intensity"],
["h", "Taste %m.buttons", "button", "mitte"]
/*,
[" ", "motor %m.leftrightall %n", "scratch_motor", "left", 50],
[" ", "leds next dial %m.leftright", "scratch_next_dial", "left"],
[" ", "leds dial all %n %n %n %n %n %n %n %n", "V_leds_circle", 0, 8, 16, 32, 0, 8, 16, 32],
[" ", "play sound SD %n", "A_sound_play_sd", ""],
[" ", "record sound %n", "A_sound_record", ""],
[" ", "replay sound %n", "A_sound_replay", ""],
[" ", "odometer %n %n %n", "Q_set_odometer", 90, 0, 0],
[" ", "leds sensors h %n %n %n %n %n %n %n %n", "V_leds_prox_h", 0, 16, 32, 32, 16, 0, 32, 32],
[" ", "leds sensors v %n %n", "V_leds_prox_v", 32, 32],
[" ", "leds buttons %n %n %n %n", "V_leds_buttons", 16, 32, 16, 32],
[" ", "leds temperature %n %n", "V_leds_temperature", 32, 8],
[" ", "leds sound %n", "V_leds_sound", 32],
[" ", "leds rc %n", "V_leds_rc", 16],
[" ", "emit %n", "emit", 1],
["r", "receive", "receive"],
["R", "temperature", "temperature"],
["r", "measure motor %m.leftright", "motor", "left"],
/* ["r", "motor %m.leftright speed", "motor_speed", "left" ],
["r", "motor %m.leftright target", "motor_target", "left" ],*/
/*
["r", "distance %m.sensors", "distance", "front"],
["r", "angle %m.angles", "angle", "front"],
["r", "proximity sensors", "prox_horizontal"],
["r", "ground sensors", "prox_ground_delta"],
["r", "leds color %m.light", "leds", "top"],
["r", "odometer %m.odo", "odo", "direction"],
["b", "object detected %m.sensors", "touching", "front"],
["b", "object detected %m.sensors %n", "touching_threshold", "front"],
*/
]
};
var menus = {
en: {
leftrightall: ["left", "right", "all"],
leftright: ["left", "right"],
sensors: ["front", "back", "ground"],
proxsensors: ["front far left", "front left", "front center", "front right", "front far right", "back left", "back right"],
singlelight: ["top", "bottom-left", "bottom-right"],
light: ["all", "top", "bottom", "bottom-left", "bottom-right"],
angles: ["front", "back", "ground"],
sounds: ["0", "1", "2", "3", "4", "5", "6", "7"],
odo: ["direction", "x", "y"],
tilts: ["front-back", "top-bottom", "left-right"],
buttons: ["center","front","back","left","right"]
},
fr: {
leftrightall: ["gauche", "droite", "tous"],
leftright: ["gauche", "droite"],
sensors: ["devant", "derrière", "dessous"],
proxsensors: ["devant extrême gauche", "devant gauche", "devant centre", "devant droite", "devant extrême droite", "derrière gauche", "derrière droite"],
singlelight: ["dessus", "dessous gauche", "dessous droite"],
light: ["tout", "dessus", "dessous", "dessous gauche", "dessous droite"],
angles: ["devant", "derrière", "dessous"],
sounds: ["0", "1", "2", "3", "4", "5", "6", "7"],
odo: ["direction", "x", "y"],
tilts: ["devant-derrière", "dessus-dessous", "gauche-droite à plat"],
buttons: ["central","devant","derrière", "gauche", "droite"]
},
it: {
leftrightall: ["sinistro", "destro", "tutti"],
leftright: ["sinistro", "destro"],
sensors: ["davanti", "dietro", "terreno"],
proxsensors: ["tutto a sinistra", "a sinistra", "centrale", "a destra", "tutto a destra", "posteriore sinistro", "posteriore destro" ],
singlelight: ["sopra", "sotto a sinistra", "sotto a destra"],
light: ["tutti", "superiori", "inferiori", "inferiori a sinistra", "inferiori a destra"],
angles: ["davanti", "dietro", "terreno"],
sounds: ["0", "1", "2", "3", "4", "5", "6", "7"],
odo: ["direzione", "x", "y"],
tilts: ["davanti-dietro", "sopra-sotto", "sinistro-destro"],
buttons: ["centrale","davanti","dietro","sinistra","destra"]
},
de: {
leftrightall: ["links", "rechts", "alle"],
leftright: ["links", "rechts"],
sensors: ["vorne", "hinten", "unten"],
proxsensors: ["vorne ganz links", "vorne links", "vorne mitte", "vorne rechts", "vorne ganz rechts", "hinten links", "hinten rechts"],
singlelight: ["oben", "unten links", "unten rechts"],
light: ["alle", "oben", "unten", "unten links", "unten rechts"],
angles: ["vorne", "hinten", "unten"],
sounds: ["0", "1", "2", "3", "4", "5", "6", "7"],
odo: ["Richtung", "x", "y"],
tilts: ["vorne nach hinten", "oben nach unten", "links nach rechts"],
buttons: ["mitte", "vorne", "hinten", "links","rechts"]
}
};
var descriptor = {
blocks: blocks[lang],
menus: menus[lang]
};
// Register the extension
ScratchExtensions.register('Thymio', descriptor, ext);
//Utilities
function clamp(val, min, max) {
return val = (val < min ? min : (val > max ? max : val));
}
/**
* The function send a Request to thymio-II REST API. It is used by command blocks that wait.
* @param {array} args - arguments to send
* @param {number} method - method type (1-GET,2-POST,3-PUT)
* @param {function} callback - callback function
*/
function requestSend(args, method, callback) {
switch (method) {
case 1:
method = 'GET';
break;
case 2:
method = 'POST';
break;
case 3:
method = 'PUT';
break;
default:
method = 'GET';
break;
}
// First argument is node name
url = ASEBAHTTPURL + 'nodes/thymio-II/' + args[0];
var req = new XMLHttpRequest();
if (!req) {
return;
}
req.open(method, url, true);
req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
req.onreadystatechange = function() {
if (req.readyState != 4) {
return;
}
callback(req);
};
if (req.readyState == 4) {
return;
}
quid = 3;
payload = '';
payload += '&body[]=' + quid;
payload += '&body[]=' + args[1];
payload += '&body[]=' + args[2];
payload += '&body[]=' + args[3];
// Send http request
req.send('[' + quid + ',' + parseInt(args[1]) + ',' + parseInt(args[2]) + ',' + parseInt(args[3]) + ']');
};
function sendAction(action, args, callback) {
var params = args.join("/")
$.ajax({
url: ASEBAHTTPURL + 'nodes/thymio-II/' + action + '/' + params,
dataType: 'json',
success: function(data) {
if (DEBUG) {
console.log("send: " + ASEBAHTTPURL + 'nodes/thymio-II/' + action + '/' + params);
}
if (typeof callback == 'function') {
if (DEBUG) {
console.log("call callback");
}
callback(data);
} else {
if (DEBUG) {
console.log("NO call callback");
}
}
}
});
}
function makeLedsRGBVector(color) {
var rgb = [];
switch (parseInt(color / 33)) {
case 0:
rgb[0] = 33;
rgb[1] = color % 33;
rgb[2] = 0;
break;
case 1:
rgb[0] = 33 - color % 33;
rgb[1] = 33;
rgb[2] = 0;
break;
case 2:
rgb[0] = 0;
rgb[1] = 33;
rgb[2] = color % 33;
break;
case 3:
rgb[0] = 0;
rgb[1] = 33 - color % 33;
rgb[2] = 33;
break;
case 4:
rgb[0] = color % 33;
rgb[1] = 0;
rgb[2] = 33;
break;
case 5:
rgb[0] = 33;
rgb[1] = 0;
rgb[2] = 33 - color % 33;
break;
}
return rgb;
}
})({});