window.birdbrain = {}; window.birdbrain.sensorData = {}; window.birdbrain.sensorData.A = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; window.birdbrain.sensorData.B = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; window.birdbrain.sensorData.C = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; window.birdbrain.microbitIsV2 = {}; window.birdbrain.microbitIsV2.A = false; window.birdbrain.microbitIsV2.B = false; window.birdbrain.microbitIsV2.C = false; window.birdbrain.currentBeak = {}; window.birdbrain.currentBeak.A = [0,0,0]; window.birdbrain.currentBeak.B = [0,0,0]; window.birdbrain.currentBeak.C = [0,0,0]; window.birdbrain.robotType = { FINCH: 1, HUMMINGBIRDBIT: 2, MICROBIT: 3, GLOWBOARD: 4, //connected robots default to type MICROBIT A: 3, B: 3, C: 3 }; //For the old style robots that connect over hid. window.bbtLegacy = {}; window.bbtLegacy.sensorData = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; console.log("setting up message channel") window.birdbrain.messageChannel = new MessageChannel(); window.birdbrain.messageChannel.port1.onmessage = function (e) { //console.log("Got a message: "); //console.log(e.data); if (e.data.sensorData != null && e.data.robot != null) { let robot = e.data.robot; window.birdbrain.sensorData[robot] = e.data.sensorData; window.birdbrain.robotType[robot] = e.data.robotType; window.birdbrain.microbitIsV2[robot] = e.data.hasV2Microbit; } if (e.data.hidSensorData != null ) { window.bbtLegacy.sensorData = e.data.hidSensorData; } } window.parent.postMessage("hello from snap", "*", [window.birdbrain.messageChannel.port2]); window.birdbrain.sendCommand = function(command) { window.parent.postMessage(command, "*"); } // Converts byte range 0 - 255 to -127 - 127 represented as a 32 bit signe int function byteToSignedInt8 (byte) { var sign = (byte & (1 << 7)); var value = byte & 0x7F; if (sign) { value = byte | 0xFFFFFF00; } return value; } // Converts byte range 0 - 255 to -127 - 127 represented as a 32 bit signe int function byteToSignedInt16 (msb, lsb) { var sign = msb & (1 << 7); var value = (((msb & 0xFF) << 8) | (lsb & 0xFF)); if (sign) { value = 0xFFFF0000 | value; // fill in most significant bits with 1's } return value; } window.birdbrain.getMicrobitAcceleration = function(axis, robot) { const rawToMperS = 196/1280; //convert to meters per second squared let sensorData = window.birdbrain.sensorData[robot]; let accVal = 0; switch (axis) { case 'X': accVal = byteToSignedInt8(sensorData[4]); break; case 'Y': accVal = byteToSignedInt8(sensorData[5]); break; case 'Z': accVal = byteToSignedInt8(sensorData[6]); break; } return (accVal * rawToMperS); } window.birdbrain.getMicrobitMagnetometer = function(axis, finch) { const rawToUT = 1/10; //convert to uT let sensorData = window.birdbrain.sensorData[finch]; let msb = 0; let lsb = 0; switch (axis) { case 'X': msb = sensorData[8]; lsb = sensorData[9]; break; case 'Y': msb = sensorData[10]; lsb = sensorData[11]; break; case 'Z': msb = sensorData[12]; lsb = sensorData[13]; break; } let magVal = byteToSignedInt16(msb, lsb); return Math.round(magVal * rawToUT); } window.birdbrain.getFinchAcceleration = function(axis, finch) { let sensorData = window.birdbrain.sensorData[finch]; let accVal = 0; switch (axis) { case 'X': accVal = byteToSignedInt8(sensorData[13]); break; case 'Y': case 'Z': const rawY = byteToSignedInt8(sensorData[14]); const rawZ = byteToSignedInt8(sensorData[15]); const rad = 40 * Math.PI / 180; //40° in radians switch(axis) { case 'Y': accVal = (rawY*Math.cos(rad) - rawZ*Math.sin(rad)); break; case 'Z': accVal = (rawY*Math.sin(rad) + rawZ*Math.cos(rad)); break; } } return (accVal * 196/1280); } window.birdbrain.getFinchMagnetometer = function(axis, finch) { let sensorData = window.birdbrain.sensorData[finch]; switch (axis) { case 'X': return byteToSignedInt8(sensorData[17]); case 'Y': case 'Z': const rawY = byteToSignedInt8(sensorData[18]); const rawZ = byteToSignedInt8(sensorData[19]); const rad = 40 * Math.PI / 180 //40° in radians let magVal = 0; switch(axis) { case 'Y': magVal = (rawY*Math.cos(rad) + rawZ*Math.sin(rad)); break; case 'Z': magVal = (rawZ*Math.cos(rad) - rawY*Math.sin(rad)); break; } return Math.round(magVal); } } //// Motion Blocks //// SnapExtensions.primitives.set( 'bbt_bitpositionservo(robot, port, position)', function (robot, port, position) { position = Math.max(0, Math.min(180, position)); position = Math.round(1.41 * position);//254/180 Scaling Factor var thisCommand = { robot: robot, cmd: "servo", port: port, value: position } window.birdbrain.sendCommand(thisCommand); } ); SnapExtensions.primitives.set( 'bbt_bitrotationservo(robot, port, speed)', function (robot, port, speed) { speed = Math.max(-100, Math.min(100, speed)); if (speed < 10 && speed > -10) { speed = 255; } else { speed = Math.round((speed * 23/100) + 122) } var thisCommand = { robot: robot, cmd: "servo", port: port, value: speed } window.birdbrain.sendCommand(thisCommand); } ); SnapExtensions.primitives.set( 'bbt_finchismoving(robot)', function (robot) { return (window.birdbrain.sensorData[robot][4] > 127); } ); SnapExtensions.primitives.set( 'bbt_finchmove(robot, direction, distance, speed)', function (robot, direction, distance, speed) { distance = Math.max(-10000, Math.min(10000, distance)); speed = Math.max(0, Math.min(100, speed)); var thisCommand = { robot: robot, cmd: "move", direction: direction, distance: distance, speed: speed } window.birdbrain.sendCommand(thisCommand) } ); SnapExtensions.primitives.set( 'bbt_finchstop(robot)', function (robot) { var thisCommand = { robot: robot, cmd: "stopFinch" } window.birdbrain.sendCommand(thisCommand) } ); SnapExtensions.primitives.set( 'bbt_finchturn(robot, direction, angle, speed)', function (robot, direction, angle, speed) { angle = Math.max(-360000, Math.min(360000, angle)); speed = Math.max(0, Math.min(100, speed)); var thisCommand = { robot: robot, cmd: "turn", direction: direction, angle: angle, speed: speed } window.birdbrain.sendCommand(thisCommand) } ); SnapExtensions.primitives.set( 'bbt_finchwheels(robot, left, right)', function (robot, left, right) { left = Math.max(-100, Math.min(100, left)); right = Math.max(-100, Math.min(100, right)); var thisCommand = { robot: robot, cmd: "wheels", speedL: left, speedR: right } window.birdbrain.sendCommand(thisCommand) } ); //// Looks Blocks //// SnapExtensions.primitives.set( 'bbt_display(robot, symbol)', function (robot, symbolString) { var thisCommand = { robot: robot, cmd: "symbol", symbolString: symbolString } window.birdbrain.sendCommand(thisCommand); } ); SnapExtensions.primitives.set( 'bbt_led(robot, port, intensity)', function (robot, port, intensity) { var thisCommand = { robot: robot, cmd: "led", port: port, intensity: Math.floor(Math.max(Math.min(intensity*2.55, 255), 0)) } window.birdbrain.sendCommand(thisCommand); } ); SnapExtensions.primitives.set( 'bbt_print(robot, string)', function (robot, string) { var thisCommand = { robot: robot, cmd: "print", printString: string } window.birdbrain.sendCommand(thisCommand); } ); SnapExtensions.primitives.set( 'bbt_triled(robot, port, red, green, blue)', function (robot, port, red, green, blue) { var thisCommand = { robot: robot, cmd: "triled", port: port, red: Math.floor(Math.max(Math.min(red*2.55, 255), 0)), green: Math.floor(Math.max(Math.min(green*2.55, 255), 0)), blue: Math.floor(Math.max(Math.min(blue*2.55, 255), 0)) } if (port == 1) { window.birdbrain.currentBeak[robot] = [thisCommand.red, thisCommand.green, thisCommand.blue]; } window.birdbrain.sendCommand(thisCommand); } ); //// Control Blocks //// SnapExtensions.primitives.set( 'bbt_stop(robot)', function (robot) { var thisCommand = { robot: robot, cmd: "stopAll" } window.birdbrain.sendCommand(thisCommand) } ); //// Sound Blocks //// SnapExtensions.primitives.set( 'bbt_playnote(robot, note, duration)', function (robot, note, duration) { note = Math.round(Math.max(32, Math.min(135, note))); console.log("playing note " + note); var thisCommand = { robot: robot, cmd: "playNote", note: note, duration: duration } window.birdbrain.sendCommand(thisCommand) } ); //// Sensing Blocks //// SnapExtensions.primitives.set( 'bbt_accelerometer(robot, dimension)', function (robot, dim) { let acc = window.birdbrain.getMicrobitAcceleration(dim, robot); return Math.round(acc * 10) / 10; } ); SnapExtensions.primitives.set( 'bbt_bitsensor(robot, sensor, port)', function (robot, sensor, port) { const distanceScaling = 117/100; const dialScaling = 100/230; const lightScaling = 100/255; const soundScaling = 200/255; const voltageScaling = 3.3/255; let value = window.birdbrain.sensorData[robot][port - 1]; switch(sensor) { case "Distance (cm)": return Math.round(value * distanceScaling); case "Dial": if (value > 230) { value = 230; } return Math.round(value * dialScaling); case "Light": return Math.round(value * lightScaling); case "Sound": return Math.round(value * soundScaling); case "Other (V)": return Math.round(value * voltageScaling * 100) / 100; default: console.log("Unknown sensor: " + sensor); return 0; } } ); SnapExtensions.primitives.set( 'bbt_button(robot, button)', function (robot, button) { const type = window.birdbrain.robotType[robot]; const index = (type == window.birdbrain.robotType.FINCH) ? 16 : 7; var buttonState = window.birdbrain.sensorData[robot][index] & 0xF0; //Button Byte position = 7, clear LS Bits as it is for shake and calibrate switch (button) { case 'A': return (buttonState == 0x00 || buttonState == 0x20) case 'B': return (buttonState == 0x00 || buttonState == 0x10) case 'Logo (V2)': if(window.birdbrain.microbitIsV2[robot]) { return (((window.birdbrain.sensorData[robot][index] >> 1) & 0x1) == 0x0) } else { return "micro:bit V2 required"; } default: console.log("unknown button " + button); return false; } } ); SnapExtensions.primitives.set( 'bbt_compass(robot)', function (robot) { const ax = window.birdbrain.getMicrobitAcceleration('X', robot); const ay = window.birdbrain.getMicrobitAcceleration('Y', robot); const az = window.birdbrain.getMicrobitAcceleration('Z', robot); const mx = window.birdbrain.getMicrobitMagnetometer('X', robot); const my = window.birdbrain.getMicrobitMagnetometer('Y', robot); const mz = window.birdbrain.getMicrobitMagnetometer('Z', robot); const phi = Math.atan(-ay / az) const theta = Math.atan(ax / (ay * Math.sin(phi) + az * Math.cos(phi))) const xp = mx const yp = my * Math.cos(phi) - mz * Math.sin(phi) const zp = my * Math.sin(phi) + mz * Math.cos(phi) const xpp = xp * Math.cos(theta) + zp * Math.sin(theta) const ypp = yp const angle = 180.0 + ((Math.atan2(xpp, ypp)) * (180 / Math.PI)) //convert result to degrees return Math.round(angle) } ); SnapExtensions.primitives.set( 'bbt_finchaccelerometer(robot, dimension)', function (robot, dim) { let acc = window.birdbrain.getFinchAcceleration(dim, robot); return Math.round(acc * 10) / 10; } ); SnapExtensions.primitives.set( 'bbt_finchcompass(robot)', function (robot) { const ax = window.birdbrain.getFinchAcceleration('X', robot); const ay = window.birdbrain.getFinchAcceleration('Y', robot); const az = window.birdbrain.getFinchAcceleration('Z', robot); const mx = window.birdbrain.getFinchMagnetometer('X', robot); const my = window.birdbrain.getFinchMagnetometer('Y', robot); const mz = window.birdbrain.getFinchMagnetometer('Z', robot); const phi = Math.atan(-ay / az) const theta = Math.atan(ax / (ay * Math.sin(phi) + az * Math.cos(phi))) const xp = mx const yp = my * Math.cos(phi) - mz * Math.sin(phi) const zp = my * Math.sin(phi) + mz * Math.cos(phi) const xpp = xp * Math.cos(theta) + zp * Math.sin(theta) const ypp = yp const angle = 180.0 + ((Math.atan2(xpp, ypp)) * (180 / Math.PI)) //convert result to degrees return ((Math.round(angle) + 180) % 360) //turn so that beak points north } ); SnapExtensions.primitives.set( 'bbt_finchdistance(robot)', function (robot) { if (window.birdbrain.microbitIsV2[robot]) { return window.birdbrain.sensorData[robot][1]; } else { const cmPerDistance = 0.0919; const msb = window.birdbrain.sensorData[robot][0]; const lsb = window.birdbrain.sensorData[robot][1]; const distance = msb << 8 | lsb; return Math.round(distance * cmPerDistance); } } ); SnapExtensions.primitives.set( 'bbt_finchencoder(robot, port)', function (robot, port) { let TICKS_PER_ROTATION = 792 var msb = 0; var ssb = 0; var lsb = 0; switch (port) { case 'Right': msb = window.birdbrain.sensorData[robot][10]; ssb = window.birdbrain.sensorData[robot][11]; lsb = window.birdbrain.sensorData[robot][12]; break; case 'Left': msb = window.birdbrain.sensorData[robot][7]; ssb = window.birdbrain.sensorData[robot][8]; lsb = window.birdbrain.sensorData[robot][9]; break; default: console.log("unknown encoder port " + port); } var encoder = msb << 16 | ssb << 8 | lsb if (encoder >= 0x800000) { encoder = encoder | 0xFF000000; } return Math.round( encoder * 10 / TICKS_PER_ROTATION ) / 10 } ); SnapExtensions.primitives.set( 'bbt_finchencoderreset(robot)', function (robot) { var thisCommand = { robot: robot, cmd: "resetEncoders" } window.birdbrain.sendCommand(thisCommand) } ); SnapExtensions.primitives.set( 'bbt_finchlight(robot, port)', function (robot, port) { const beak = window.birdbrain.currentBeak[robot] || [0,0,0]; const R = beak[0]*100/255; const G = beak[1]*100/255; const B = beak[2]*100/255; var raw = 0; var correction = 0; switch (port) { case 'Right': raw = window.birdbrain.sensorData[robot][3]; correction = 6.40473070e-03*R + 1.41015162e-02*G + 5.05547817e-02*B + 3.98301391e-04*R*G + 4.41091223e-04*R*B + 6.40756862e-04*G*B + -4.76971242e-06*R*G*B; break; case 'Left': raw = window.birdbrain.sensorData[robot][2]; correction = 1.06871493e-02*R + 1.94526614e-02*G + 6.12409825e-02*B + 4.01343475e-04*R*G + 4.25761981e-04*R*B + 6.46091068e-04*G*B + -4.41056971e-06*R*G*B; break; default: console.log("unknown light port " + port); return 0; } return Math.round(Math.max(0, Math.min(100, (raw - correction)))); } ); SnapExtensions.primitives.set( 'bbt_finchline(robot, port)', function (robot, port) { var rawVal = 0; switch (port) { case 'Right': rawVal = window.birdbrain.sensorData[robot][5]; break; case 'Left': rawVal = window.birdbrain.sensorData[robot][4]; //first bit is for position control rawVal = (0x7F & rawVal) break; default: console.log("unknown line port " + port); } var returnVal = 100 - ((rawVal - 6) * 100/121); return Math.min(100, Math.max(0, Math.round(returnVal))); } ); SnapExtensions.primitives.set( 'bbt_finchmagnetometer(robot, dimension)', function (robot, dim) { return window.birdbrain.getFinchMagnetometer(dim, robot); } ); SnapExtensions.primitives.set( 'bbt_finchorientation(robot, dimension)', function (robot, dim) { if (dim == "Shake") { let shake = window.birdbrain.sensorData[robot][16]; return ((shake & 0x01) > 0); } const threshold = 7.848; let acceleration = 0; switch(dim) { case "Tilt Left": case "Tilt Right": acceleration = window.birdbrain.getFinchAcceleration('X', robot); break; case "Beak Up": case "Beak Down": acceleration = window.birdbrain.getFinchAcceleration('Y', robot); break; case "Level": case "Upside Down": acceleration = window.birdbrain.getFinchAcceleration('Z', robot); break; } switch(dim) { case "Tilt Right": case "Beak Up": case "Upside Down": return (acceleration > threshold); case "Tilt Left": case "Beak Down": case "Level": return (acceleration < -threshold); } console.log("Unknown dimension " + dim); return false; } ); SnapExtensions.primitives.set( 'bbt_magnetometer(robot, dimension)', function (robot, dim) { return window.birdbrain.getMicrobitMagnetometer(dim, robot); } ); SnapExtensions.primitives.set( 'bbt_orientation(robot, dimension)', function (robot, dim) { if (dim == "Shake") { const index = 7; let shake = window.birdbrain.sensorData[robot][index]; return ((shake & 0x01) > 0); } const threshold = 7.848; let acceleration = 0; switch(dim) { case "Tilt Left": case "Tilt Right": acceleration = window.birdbrain.getMicrobitAcceleration('X', robot); break; case "Logo Up": case "Logo Down": acceleration = window.birdbrain.getMicrobitAcceleration('Y', robot); break; case "Screen Up": case "Screen Down": acceleration = window.birdbrain.getMicrobitAcceleration('Z', robot); break; } switch(dim) { case "Tilt Left": case "Logo Down": case "Screen Down": return (acceleration > threshold); case "Tilt Right": case "Logo Up": case "Screen Up": return (acceleration < -threshold); } console.log("Unknown dimension " + dim); return false; } ); SnapExtensions.primitives.set( 'bbt_sound(robot)', function (robot) { if (window.birdbrain.microbitIsV2[robot]) { const type = window.birdbrain.robotType[robot]; if (type == window.birdbrain.robotType.FINCH) { return window.birdbrain.sensorData[robot][0]; } else { return window.birdbrain.sensorData[robot][14]; } } else { return "micro:bit V2 required" } } ); SnapExtensions.primitives.set( 'bbt_temperature(robot)', function (robot) { if (window.birdbrain.microbitIsV2[robot]) { const type = window.birdbrain.robotType[robot]; if (type == window.birdbrain.robotType.FINCH) { return (window.birdbrain.sensorData[robot][6] >> 2); } else { return window.birdbrain.sensorData[robot][15]; } } else { return "micro:bit V2 required" } } ); //// GlowBoard Blocks //// SnapExtensions.primitives.set( 'bbt_gbbutton(robot, button)', function (robot, button) { var buttonState = window.birdbrain.sensorData[robot][5] & 0xF0; //Button Byte position = 7, clear LS Bits as it is for shake and calibrate switch (button) { case 'right': return (buttonState == 0x00 || buttonState == 0x20) case 'left': return (buttonState == 0x00 || buttonState == 0x10) default: console.log("unknown button " + button); return false; } } ); SnapExtensions.primitives.set( 'bbt_gbdial(robot, dial)', function (robot, sensor) { let index = 1 if (sensor == "Right") { index = 3 } const msb = window.birdbrain.sensorData[robot][index]; const lsb = window.birdbrain.sensorData[robot][index + 1]; const value = msb << 8 | lsb; return value; } ); SnapExtensions.primitives.set( 'bbt_gbdisplay(robot, color, brightness, symbol)', function (robot, color, brightness, symbolString) { var thisCommand = { robot: robot, cmd: "glowboard", color: color, brightness: brightness, symbolString: symbolString } window.birdbrain.sendCommand(thisCommand); } ); SnapExtensions.primitives.set( 'bbt_gbsetpoint(robot, X, Y, color, brightness)', function (robot, xPos, yPos, color, brightness) { var thisCommand = { robot: robot, cmd: "setPoint", xPos: Math.round(Math.max(Math.min(xPos, 12), 1)), yPos: Math.round(Math.max(Math.min(yPos, 12), 1)), color: color, brightness: brightness } window.birdbrain.sendCommand(thisCommand); } ); //// Blocks for old style robots //// SnapExtensions.primitives.set( 'bbt_legacyled(port, intensity)', function (portnum, intensitynum) { var realPort = portnum-1; var realIntensity = Math.floor(intensitynum*2.55); var report = { message:"L".charCodeAt(0), port: realPort.toString().charCodeAt(0), intensity: realIntensity }; window.birdbrain.sendCommand( report ); } ); SnapExtensions.primitives.set( 'bbt_legacytriled(port, red, green, blue)', function (portnum, rednum, greennum, bluenum) { var realPort = portnum-1; var realIntensities = [rednum, greennum, bluenum].map(function(intensity) { return Math.floor(Math.max(Math.min(intensity*2.55, 255), 0)); }); var report = { message:"O".charCodeAt(0), port: realPort.toString().charCodeAt(0), red: realIntensities[0], green: realIntensities[1], blue: realIntensities[2] }; window.birdbrain.sendCommand( report ); } ); SnapExtensions.primitives.set( 'bbt_legacyservo(port, position)', function (portnum, ang) { var realPort = portnum-1; var realAngle = Math.floor(ang*1.25); realAngle = Math.max(Math.min(realAngle,225.0),0.0); var report = { message: "S".charCodeAt(0), port: realPort.toString().charCodeAt(0), angle: realAngle }; window.birdbrain.sendCommand( report ); } ); SnapExtensions.primitives.set( 'bbt_legacymotor(port, speed)', function (portnum, velocity) { var realPort = portnum-1; var realVelocity = Math.floor(velocity*2.55); realVelocity = Math.max(Math.min(realVelocity,255), -255); var report = { message: "M".charCodeAt(0), port: realPort.toString().charCodeAt(0), direction: (realVelocity < 0 ? 1 : 0).toString().charCodeAt(0), velocity: Math.abs(realVelocity) }; window.birdbrain.sendCommand( report ); } ); SnapExtensions.primitives.set( 'bbt_legacyvibration(port, intensity)', function (portnum, intensitynum) { var realPort = portnum-1; var realIntensity = Math.floor(intensitynum*2.55); realIntensity = Math.max(Math.min(realIntensity,255.0),0.0); var report = { message: "V".charCodeAt(0), port: realPort.toString().charCodeAt(0), intensity: realIntensity }; window.birdbrain.sendCommand( report ); } ); SnapExtensions.primitives.set( 'bbt_legacysaythis(phrase)', function (phrase) { var report = { message: "SPEAK", val: phrase}; window.birdbrain.sendCommand( report ); } ); SnapExtensions.primitives.set( 'bbt_legacyhbsensor(sensor, port)', function (sensor, port) { var realport = port - 1; var sensorvalue = window.bbtLegacy.sensorData[realport] switch(sensor) { case "Light": return parseInt(sensorvalue / 2.55); case "Temperature": //Celsius return Math.floor(((sensorvalue-127)/2.4+25)*100/100); case "Distance": //cm var reading = sensorvalue*4; if (reading < 130) { sensorvalue = 100; } else { //formula based on mathematical regression reading = reading - 120; var distance; if (reading > 680) { distance = 5.0; } else { var sensor_val_square = reading*reading; distance = sensor_val_square*sensor_val_square*reading*-0.000000000004789 + sensor_val_square*sensor_val_square*0.000000010057143 - sensor_val_square*reading*0.000008279033021 + sensor_val_square*0.003416264518201 - reading*0.756893112198934 + 90.707167605683000; } sensorvalue = parseInt(distance); } return sensorvalue; case "Dial": return parseInt(sensorvalue / 2.55); case "Sound": if (sensorvalue > 14) { return (sensorvalue - 15) * 3/2 } else { return 0 } case "Raw": return parseInt(sensorvalue / 2.55); default: console.log("Unknown sensor: " + sensor); return 0; } } ); SnapExtensions.primitives.set( 'bbt_legacyfinchmove(left, right)', function (left, right) { function constrain(n) { return Math.max(Math.min(n, 255), -255); } var speeds = [constrain(Math.round(left * 2.55)), constrain(Math.round(right * 2.55))]; var report = { message: "M".charCodeAt(0), leftDirection: speeds[0] < 0 ? 1 : 0, leftSpeed: Math.abs(speeds[0]), rightDirection: speeds[1] < 0 ? 1 : 0, rightSpeed: Math.abs(speeds[1]), }; window.birdbrain.sendCommand( report ); } ); SnapExtensions.primitives.set( 'bbt_legacyfinchled(red, green, blue)', function (red, green, blue) { // constrain n to the range [0..255] function constrain(n) { return Math.max(Math.min(n, 255), 0); } var values = [constrain(Math.round(red * 2.55)), constrain(Math.round(green * 2.55)), constrain(Math.round(blue * 2.55))]; var report = { message: "O".charCodeAt(0), red: values[0], green: values[1], blue: values[2] }; window.birdbrain.sendCommand( report ); } ); SnapExtensions.primitives.set( 'bbt_legacyfinchbuzzer(frequency, duration)', function (freq, time) { //constrain n to the range [0..65535] function constrain(n) { return Math.max(Math.min(n, 0xFFFF), 0); } var value = { freq: constrain(Math.round(freq)), time: constrain(Math.round(time)) }; var report = { message: "B".charCodeAt(0), timeHigh: value.time >> 8, // Since the report must be in bytes timeLow: value.time & 0xFF, // and these values are bigger than a byte freqHigh: value.freq >> 8, // they are split into two bytes freqLow: value.freq & 0xFF }; window.birdbrain.sendCommand( report ); } ); SnapExtensions.primitives.set( 'bbt_legacyfinchsensor(port)', function (port) { //Ports: Left Light = 0; Right Light = 1; // Acceleration (X, Y, Z) = (2, 3, 4); // Left Obstacle = 5; Right Obstacle = 6; // Temperature C = 7; return window.bbtLegacy.sensorData[port]; } ); SnapExtensions.primitives.set( 'bbt_legacyfinchorientation()', function () { var acceleration = Array(3); acceleration[0] = window.bbtLegacy.sensorData[2] acceleration[1] = window.bbtLegacy.sensorData[3] acceleration[2] = window.bbtLegacy.sensorData[4] var orientation; if(acceleration[0] > -0.5 && acceleration[0] < 0.5 && acceleration[1] < 0.5 && acceleration[1] > -0.5 && acceleration[2] > 0.65 && acceleration[2] < 1.5) orientation = "level"; else if(acceleration[0] > -0.5 && acceleration[0] < 0.5 && acceleration[1] < 0.5 && acceleration[1] > -0.5 && acceleration[2] > -1.5 && acceleration[2] < -0.65) orientation = "upside down"; else if(acceleration[0] < 1.5 && acceleration[0] > 0.8 && acceleration[1] > -0.3 && acceleration[1] < 0.3 && acceleration[2] > -0.3 && acceleration[2] < 0.3) orientation = "beak down"; else if(acceleration[0] < -0.8 && acceleration[0] > -1.5 && acceleration[1] > -0.3 && acceleration[1] < 0.3 && acceleration[2] > -0.3 && acceleration[2] < 0.3) orientation = "beak up"; else if(acceleration[0] > -0.5 && acceleration[0] < 0.5 && acceleration[1] > 0.7 && acceleration[1] < 1.5 && acceleration[2] > -0.5 && acceleration[2] < 0.5) orientation = "left wing down"; else if(acceleration[0] > -0.5 && acceleration[0] < 0.5 && acceleration[1] > -1.5 && acceleration[1] < -0.7 && acceleration[2] > -0.5 && acceleration[2] < 0.5) orientation = "right wing down"; else orientation = "in between"; return orientation } );