const enum GyroSensorMode { None = 0, Angle = 0, Rate = 1, } namespace sensors { //% fixedInstances export class GyroSensor extends internal.UartSensor { private _calibrating: boolean; private _drift: number; private _angle: control.EulerIntegrator; constructor(port: number) { super(port) this._calibrating = false; this._drift = 0; this._angle = new control.EulerIntegrator(); this._setMode(GyroSensorMode.Rate); this.setMode(GyroSensorMode.Rate); } _deviceType() { return DAL.DEVICE_TYPE_GYRO } _query(): number { const v = this.getNumber(NumberFormat.Int16LE, 0); this._angle.integrate(v - this._drift); return v; } setMode(m: GyroSensorMode) { // decrecated } isCalibrating(): boolean { return this._calibrating; } /** * Get the current angle from the gyroscope. * @param sensor the gyroscope to query the request */ //% help=sensors/gyro/angle //% block="**gyro** %this|angle" //% blockId=gyroGetAngle //% parts="gyroscope" //% blockNamespace=sensors //% this.fieldEditor="ports" //% weight=64 blockGap=8 //% group="Gyro Sensor" angle(): number { this.poke(); if (this._calibrating) pauseUntil(() => !this._calibrating, 2000); return Math.round(this._angle.value); } /** * Get the current rotation rate from the gyroscope. * @param sensor the gyroscope to query the request */ //% help=sensors/gyro/rate //% block="**gyro** %this|rate" //% blockId=gyroGetRate //% parts="gyroscope" //% blockNamespace=sensors //% this.fieldEditor="ports" //% weight=65 blockGap=8 //% group="Gyro Sensor" rate(): number { this.poke(); if (this._calibrating) pauseUntil(() => !this._calibrating, 2000); return this._query() - this._drift; } /** * Detects if calibration is necessary and performs a full reset, drift computation. * Must be called when the sensor is completely still. */ //% help=sensors/gyro/calibrate //% block="calibrate **gyro** %this|" //% blockId=gyroCalibrate //% parts="gyroscope" //% blockNamespace=sensors //% this.fieldEditor="ports" //% weight=51 blockGap=8 //% group="Gyro Sensor" calibrate(): void { if (this._calibrating) return; // already in calibration mode const statusLight = brick.statusLight(); // save current status light brick.setStatusLight(StatusLight.Orange); this._calibrating = true; // may be triggered by a button click, // give time for robot to settle pause(700); // compute drift this.computeDriftNoCalibration(); if (Math.abs(this.drift()) < 0.1) { // no drift, skipping calibration brick.setStatusLight(StatusLight.Green); // success pause(1000); brick.setStatusLight(statusLight); // resture previous light // and we're done this._angle.reset(); this._calibrating = false; return; } // calibrating brick.setStatusLight(StatusLight.OrangePulse); // send a reset command super.reset(); // wait till sensor is live pauseUntil(() => this.isActive(), 7000); // mode toggling this._setMode(GyroSensorMode.Rate); this._setMode(GyroSensorMode.Angle); this._setMode(GyroSensorMode.Rate); // check sensor is ready if (!this.isActive()) { brick.setStatusLight(StatusLight.RedFlash); // didn't work pause(2000); brick.setStatusLight(statusLight); // restore previous light this._angle.reset(); this._calibrating = false; return; } // switch to rate mode this.computeDriftNoCalibration(); // and done brick.setStatusLight(StatusLight.Green); // success pause(1000); brick.setStatusLight(statusLight); // resture previous light // and we're done this._angle.reset(); this._calibrating = false; } /** * Forces a calibration of the gyro. Must be called when the sensor is completely still. */ //% help=sensors/gyro/reset //% block="reset **gyro** %this|" //% blockId=gyroReset //% parts="gyroscope" //% blockNamespace=sensors //% this.fieldEditor="ports" //% weight=50 blockGap=8 //% group="Gyro Sensor" reset(): void { if (this._calibrating) return; // already in calibration mode this._calibrating = true; const statusLight = brick.statusLight(); // save current status light brick.setStatusLight(StatusLight.Orange); // send a reset command super.reset(); this._drift = 0; this._angle.reset(); pauseUntil(() => this.isActive(), 7000); // check sensor is ready if (!this.isActive()) { brick.setStatusLight(StatusLight.RedFlash); // didn't work pause(2000); brick.setStatusLight(statusLight); // restore previous light this._angle.reset(); this._calibrating = false; return; } this._setMode(GyroSensorMode.Rate); // and done brick.setStatusLight(StatusLight.Green); // success pause(1000); brick.setStatusLight(statusLight); // resture previous light // and done this._angle.reset(); this._calibrating = false; } /** * Gets the computed rate drift */ //% help=sensors/gyro/drift //% block="**gyro** %this|drift" //% blockId=gyroDrift //% parts="gyroscope" //% blockNamespace=sensors //% this.fieldEditor="ports" //% weight=9 blockGap=8 //% group="Gyro Sensor" drift(): number { return this._drift; } /** * Computes the current sensor drift when using rate measurements. */ //% help=sensors/gyro/compute-drift //% block="compute **gyro** %this|drift" //% blockId=gyroComputeDrift //% parts="gyroscope" //% blockNamespace=sensors //% this.fieldEditor="ports" //% weight=10 blockGap=8 //% group="Gyro Sensor" computeDrift() { if (this._calibrating) pauseUntil(() => !this._calibrating, 2000); pause(1000); // let the robot settle this.computeDriftNoCalibration(); } /** * Pauses the program until the gyro detected * that the angle changed by the desired amount of degrees. * @param degrees the degrees to turn */ //% help=sensors/gyro/pause-until-rotated //% block="pause until **gyro** %this|rotated %degrees=rotationPicker|degrees" //% blockId=gyroPauseUntilRotated //% parts="gyroscope" //% blockNamespace=sensors //% this.fieldEditor="ports" //% degrees.defl=90 //% weight=63 //% group="Gyro Sensor" pauseUntilRotated(degrees: number, timeOut?: number): void { let a = this.angle(); const end = a + degrees; const direction = (end - a) > 0 ? 1 : -1; pauseUntil(() => (end - this.angle()) * direction <= 0, timeOut); } private computeDriftNoCalibration() { // clear drift this._drift = 0; const n = 10; let d = 0; for (let i = 0; i < n; ++i) { d += this._query(); pause(20); } this._drift = d / n; this._angle.reset(); } _info(): string { if (this._calibrating) return "cal..."; let r = `${this._query()}r`; if (this._drift != 0) r += `-${this._drift | 0}`; return r; } } //% fixedInstance whenUsed block="2" weight=95 jres=icons.port2 export const gyro2: GyroSensor = new GyroSensor(2) //% fixedInstance whenUsed block="1" jres=icons.port1 export const gyro1: GyroSensor = new GyroSensor(1) //% fixedInstance whenUsed block="3" jres=icons.port3 export const gyro3: GyroSensor = new GyroSensor(3) //% fixedInstance whenUsed block="4" jres=icons.port4 export const gyro4: GyroSensor = new GyroSensor(4) /** * Get the rotation angle field editor * @param degrees angle in degrees, eg: 90 */ //% blockId=rotationPicker block="%degrees" //% blockHidden=true shim=TD_ID //% colorSecondary="#FFFFFF" //% degrees.fieldEditor="numberdropdown" degrees.fieldOptions.decompileLiterals=true //% degrees.fieldOptions.data='[["30", 30], ["45", 45], ["60", 60], ["90", 90], ["180", 180], ["-30", -30], ["-45", -45], ["-60", -60], ["-90", -90], ["-180", -180]]' export function __rotationPicker(degrees: number): number { return degrees; } }