From 891a237718546e71ad7b05073cc8b8b818f0ecc0 Mon Sep 17 00:00:00 2001 From: Dmitriy Antipov Date: Fri, 2 Feb 2024 18:33:11 +0300 Subject: [PATCH 1/2] Nxt Light Sensor support (#1027) * add-subcategories Add NXT Sensors subcategorie * first-support-version The light sensor gives real values. Mode change not supported... * update * Update ns.ts * sens-sim-support * min-and-max-metod * sim-support-new-chenges * for-sim The simulator now understands that it has an EV3 or NXT analog sensor enabled. The devType value of the virtual sensor will be passed so that the sensor becomes active in the simulator and transmits its values to the simulator. * Update input.ts * add-test * fix-convert-range-bug * Update light.ts Setting the range of values for determining black and white in reflection mode and in ambient light mode is now different. * Update test.ts * Update test.ts * Update test.ts * removing-function-doesnt-work I'm removing an unnecessary function that doesn't work, which I took from the pelikhan change. It does not apply to this change with the sensor and data input. * checking-input-values * set-nxt-light-sensor-svg * Update light.ts * update-for-sim * Update NXT Light Sensor.svg * resolving-conflict-with-master * resolving-conflict-with-master-2 * resolving-conflict-with-master-3 * Update nxtLightSensorView.ts * Update input.ts * value-range-update These numbers were obtained by testing on 4 sensors. The black value is the sensor aimed at the void, and the light value is aimed at the white Lego brick. * Update light.ts * some-changes-for-the-mode * LightWheelControl-is-always-activated-except-in-none-mode LightWheelControl is always activated except in NONE mode. Otherwise, the mode was activated immediately when the sensor was turned on in the simulator, when the operating mode had not yet been activated. * 4096-to-4095 Fix range - 0..4095 * enum-NXTLightIntensityMode * light-sensor-svg-updage * update-light-sensor-ts * ambient-modes-set-invisible Disable the visibility of ambient blocks so that they are not used, because There is no implementation of disabling LED lighting. * range-from-lego-sources https://github.com/mindboards/ev3sources-xtended/blob/master/ev3sources/lms2012/lms2012/Linux_AM1808/sys/settings/typedata.rcf * sim-fix-for-sensor Solving the problem that the field in the simulator and the value from the sensor on the screen could be different. And one more thing... * query-update-and-add-treshold-blocks * test-upd * Update lightWheel.ts * changes-light-sensor-for-sim The changes are aimed at supporting reflection and lighting modes, not raw modes. * deviceType-analog-sens-class * Update light.ts * fix-range-for-refLight-and-ambLight * export-enum-DevConOff * bug-fix-from-previous-version * sensor-activation Setting the mode so that the sensor starts working in the simulator. Otherwise, it, like a touch sensor, does not create view control, because in the touch sensor this was not necessary. Without this change, lightView would only activate if a sensor with type uart was used in the code. * Update light.ts Support blocks have been removed, which may be available later. The ambient mode has been removed, because There is no implementation of turning off the backlight LED. Now it is always on. * Update light.ts * lib-not-include-by-defl Make sure that the library is not included by default. It will need to be enabled via extensions. * light-to-bright * add-docs * Update nxt-light-sensor.md --- libs/core/dal.d.ts | 2 + libs/core/ev3const.h | 2 + libs/core/input.ts | 95 ++++++-- libs/ev3/console.ts | 4 +- libs/ev3/ns.ts | 2 +- libs/nxt-light-sensor/README.md | 3 + .../reference/sensors/nxt-light-sensor.md | 10 + .../sensors/nxt-light-sensor/light.md | 31 +++ libs/nxt-light-sensor/light.ts | 220 ++++++++++++++++++ libs/nxt-light-sensor/pxt.json | 15 ++ libs/nxt-light-sensor/test.ts | 19 ++ libs/touch-sensor/touch.ts | 4 +- pxtarget.json | 1 + sim/dalboard.ts | 1 + sim/state/analog.ts | 26 ++- sim/state/nodeTypes.ts | 3 +- sim/state/nxtlight.ts | 58 +++++ sim/state/sensor.ts | 9 +- sim/state/uart.ts | 8 +- sim/visuals/assets/NXT Light Sensor.svg | 151 ++++++++++++ sim/visuals/assets/NXT Light Sensor.ts | 150 ++++++++++++ sim/visuals/board.ts | 9 + sim/visuals/controls/lightWheel.ts | 129 ++++++++++ sim/visuals/nodes/nxtLightSensorView.ts | 42 ++++ 24 files changed, 951 insertions(+), 43 deletions(-) create mode 100644 libs/nxt-light-sensor/README.md create mode 100644 libs/nxt-light-sensor/docs/reference/sensors/nxt-light-sensor.md create mode 100644 libs/nxt-light-sensor/docs/reference/sensors/nxt-light-sensor/light.md create mode 100644 libs/nxt-light-sensor/light.ts create mode 100644 libs/nxt-light-sensor/pxt.json create mode 100644 libs/nxt-light-sensor/test.ts create mode 100644 sim/state/nxtlight.ts create mode 100644 sim/visuals/assets/NXT Light Sensor.svg create mode 100644 sim/visuals/assets/NXT Light Sensor.ts create mode 100644 sim/visuals/controls/lightWheel.ts create mode 100644 sim/visuals/nodes/nxtLightSensorView.ts diff --git a/libs/core/dal.d.ts b/libs/core/dal.d.ts index 4507f801..837de14d 100644 --- a/libs/core/dal.d.ts +++ b/libs/core/dal.d.ts @@ -309,6 +309,8 @@ declare const enum DAL { DEVICE_TYPE_NXT_LIGHT = 2, DEVICE_TYPE_NXT_SOUND = 3, DEVICE_TYPE_NXT_COLOR = 4, + DEVICE_TYPE_NXT_ULTRASONIC = 5, + DEVICE_TYPE_NXT_TEMPERATURE = 6, DEVICE_TYPE_TACHO = 7, DEVICE_TYPE_MINITACHO = 8, DEVICE_TYPE_NEWTACHO = 9, diff --git a/libs/core/ev3const.h b/libs/core/ev3const.h index 6c222ff3..53881bf6 100644 --- a/libs/core/ev3const.h +++ b/libs/core/ev3const.h @@ -11,6 +11,8 @@ #define DEVICE_TYPE_NXT_LIGHT 2 #define DEVICE_TYPE_NXT_SOUND 3 #define DEVICE_TYPE_NXT_COLOR 4 +#define DEVICE_TYPE_NXT_ULTRASONIC 5 +#define DEVICE_TYPE_NXT_TEMPERATURE 6 #define DEVICE_TYPE_TACHO 7 #define DEVICE_TYPE_MINITACHO 8 #define DEVICE_TYPE_NEWTACHO 9 diff --git a/libs/core/input.ts b/libs/core/input.ts index a56cfde7..b25abe34 100644 --- a/libs/core/input.ts +++ b/libs/core/input.ts @@ -235,10 +235,11 @@ namespace sensors.internal { function detectDevices() { control.dmesg(`DETECT DEVICES (hash ${hashDevices()})`); - const conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS); + const inDcm = analogMM.slice(AnalogOff.InDcm, DAL.NUM_INPUTS); + const inConn = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS); for (const sensorInfo of sensorInfos) { - const newConn = conns[sensorInfo.port]; + const newConn = inConn[sensorInfo.port]; if (newConn == sensorInfo.connType && sensorInfo.sensor && sensorInfo.sensor.isActive()) { continue; } @@ -252,9 +253,13 @@ namespace sensors.internal { sensorInfo.devType = DAL.DEVICE_TYPE_IIC_UNKNOWN; sensorInfo.iicid = readIICID(sensorInfo.port); control.dmesg(`new IIC connection at port ${sensorInfo.port} with ID ${sensorInfo.iicid.length}`); + } else if (newConn == DAL.CONN_NXT_DUMB) { + sensorInfo.devType = inDcm[sensorInfo.port]; + control.dmesg(`new NXT DUMB connection at port ${sensorInfo.port} dev type ${sensorInfo.devType}`); } else if (newConn == DAL.CONN_INPUT_DUMB) { - control.dmesg(`new DUMB connection at port ${sensorInfo.port}`); - sensorInfo.devType = DAL.DEVICE_TYPE_TOUCH; // TODO? for now assume touch sensor + //sensorInfo.devType = inDcm[sensorInfo.port]; // We get the result DEVICE_TYPE_UNKNOWN + sensorInfo.devType = DAL.DEVICE_TYPE_TOUCH; // TODO? for now assume touch + control.dmesg(`new DUMB connection at ${sensorInfo.port} dev type ${sensorInfo.devType}`); } else if (newConn == DAL.CONN_NONE || newConn == 0) { control.dmesg(`disconnect at port ${sensorInfo.port}`); } else { @@ -351,13 +356,44 @@ namespace sensors.internal { } export class AnalogSensor extends Sensor { + + protected mode: number; // the mode user asked for + protected realmode: number; + constructor(port: number) { - super(port) + super(port); + this.mode = 0; + this.realmode = 0; + } + + _activated() { + this.realmode = 0; + this._setMode(this.mode); + } + + protected _setMode(m: number) { + let v = m | 0; + this.mode = v; + if (!this.isActive()) return; + if (this.realmode != this.mode) { + control.dmesg(`_setMode p=${this._port} m: ${this.realmode} -> ${v}`); + this.realmode = v; + setAnalogMode(this._port, this._deviceType(), this.mode); + } + } + + _readPin1() { + if (!this.isActive()) return 0; + return analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.InPin1 + 2 * this._port); } _readPin6() { - if (!this.isActive()) return 0 - return analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.InPin6 + 2 * this._port) + if (!this.isActive()) return 0; + return analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.InPin6 + 2 * this._port); + } + + _deviceType() { + return DAL.DEVICE_TYPE_UNKNOWN; } } @@ -366,14 +402,14 @@ namespace sensors.internal { protected realmode: number // the mode the hardware is in constructor(port: number) { - super(port) - this.mode = 0 - this.realmode = 0 + super(port); + this.mode = 0; + this.realmode = 0; } _activated() { - this.realmode = 0 - this._setMode(this.mode) + this.realmode = 0; + this._setMode(this.mode); } getStatus() { @@ -381,13 +417,14 @@ namespace sensors.internal { } protected _setMode(m: number) { - //control.dmesg(`_setMode p=${this.port} m: ${this.realmode} -> ${m}`) - let v = m | 0 - this.mode = v - if (!this.isActive()) return + //control.dmesg(`_setMode p=${this.port} m: ${this.realmode} -> ${m}`); + let v = m | 0; + this.mode = v; + if (!this.isActive()) return; if (this.realmode != this.mode) { - this.realmode = v - setUartMode(this._port, v) + control.dmesg(`_setMode p=${this._port} m: ${this.realmode} -> ${v}`); + this.realmode = v; + setUartMode(this._port, v); } } @@ -575,19 +612,29 @@ namespace sensors.internal { DAL.MAX_DEVICE_DATALENGTH) } - function getUartNumber(fmt: NumberFormat, off: number, port: number) { + function getUartNumber(fmt: NumberFormat, off: number, port: number): number { if (port < 0) return 0 - let index = uartMM.getNumber(NumberFormat.UInt16LE, UartOff.Actual + port * 2) + const index = uartMM.getNumber(NumberFormat.UInt16LE, UartOff.Actual + port * 2) return uartMM.getNumber(fmt, UartOff.Raw + DAL.MAX_DEVICE_DATALENGTH * 300 * port + DAL.MAX_DEVICE_DATALENGTH * index + off) } + function setAnalogMode(port: number, type: number, mode: number) { + if (port < 0) return; + control.dmesg(`analog set type ${type} mode ${mode} at port ${port}`); + devcon.setNumber(NumberFormat.Int8LE, DevConOff.Connection + port, DAL.CONN_NXT_DUMB); + devcon.setNumber(NumberFormat.Int8LE, DevConOff.Type + port, type); + devcon.setNumber(NumberFormat.Int8LE, DevConOff.Mode + port, mode); + analogMM.ioctl(0, devcon); + } + export function setIICMode(port: number, type: number, mode: number) { if (port < 0) return; - devcon.setNumber(NumberFormat.Int8LE, DevConOff.Connection + port, DAL.CONN_NXT_IIC) - devcon.setNumber(NumberFormat.Int8LE, DevConOff.Type + port, type) - devcon.setNumber(NumberFormat.Int8LE, DevConOff.Mode + port, mode) - IICMM.ioctl(IO.IIC_SET_CONN, devcon) + control.dmesg(`iic set type ${type} mode ${mode} at port ${port}`); + devcon.setNumber(NumberFormat.Int8LE, DevConOff.Connection + port, DAL.CONN_NXT_IIC); + devcon.setNumber(NumberFormat.Int8LE, DevConOff.Type + port, type); + devcon.setNumber(NumberFormat.Int8LE, DevConOff.Mode + port, mode); + IICMM.ioctl(IO.IIC_SET_CONN, devcon); } export function transactionIIC(port: number, deviceAddress: number, writeBuf: number[], readLen: number) { diff --git a/libs/ev3/console.ts b/libs/ev3/console.ts index 8c95a185..ef28f8b4 100644 --- a/libs/ev3/console.ts +++ b/libs/ev3/console.ts @@ -19,8 +19,8 @@ namespace console._screen { if (!lines) { lines = []; console.addListener(log); - brick.buttonUp.onEvent(ButtonEvent.Bumped, () => scroll(-3)) - brick.buttonDown.onEvent(ButtonEvent.Bumped, () => scroll(3)) + brick.buttonUp.onEvent(ButtonEvent.Bumped, () => scroll(-3)); + brick.buttonDown.onEvent(ButtonEvent.Bumped, () => scroll(3)); } } diff --git a/libs/ev3/ns.ts b/libs/ev3/ns.ts index 812781b8..e2c6bf10 100644 --- a/libs/ev3/ns.ts +++ b/libs/ev3/ns.ts @@ -6,7 +6,7 @@ namespace brick { //% color="#C8509B" weight=95 icon="\uf10f" //% labelLineWidth=100 -//% groups='["Touch Sensor", "Color Sensor", "Ultrasonic Sensor", "Gyro Sensor", "Infrared Sensor", "Remote Infrared Beacon", "Calibration"]' +//% groups='["Touch Sensor", "Color Sensor", "Ultrasonic Sensor", "Gyro Sensor", "Infrared Sensor", "Remote Infrared Beacon", "Calibration", "Light Sensor"]' //% subcategories='["NXT", "HiTechnic"]' namespace sensors { } diff --git a/libs/nxt-light-sensor/README.md b/libs/nxt-light-sensor/README.md new file mode 100644 index 00000000..d0b65957 --- /dev/null +++ b/libs/nxt-light-sensor/README.md @@ -0,0 +1,3 @@ +# NXT Light sensor + +The library to interact with the NXT Light Sensor. \ No newline at end of file diff --git a/libs/nxt-light-sensor/docs/reference/sensors/nxt-light-sensor.md b/libs/nxt-light-sensor/docs/reference/sensors/nxt-light-sensor.md new file mode 100644 index 00000000..9f5fb2f1 --- /dev/null +++ b/libs/nxt-light-sensor/docs/reference/sensors/nxt-light-sensor.md @@ -0,0 +1,10 @@ +# NXT light sensor + +```cards +sensors.nxtLight1.light(NXTLightIntensityMode.Reflected) +sensors.nxtLight1.light(NXTLightIntensityMode.ReflectedRaw) +``` + +## See slso + +[light](/reference/sensors/nxt-light-sensor/light) diff --git a/libs/nxt-light-sensor/docs/reference/sensors/nxt-light-sensor/light.md b/libs/nxt-light-sensor/docs/reference/sensors/nxt-light-sensor/light.md new file mode 100644 index 00000000..a714da2d --- /dev/null +++ b/libs/nxt-light-sensor/docs/reference/sensors/nxt-light-sensor/light.md @@ -0,0 +1,31 @@ +# light + +Get the amount of ambient or reflected light measured by the sensor. + +```sig +sensors.nxtLight1.light(NXTLightIntensityMode.Reflected) +``` + +The light sensor adjusts itself to more accurately measure light depending on the source of the light. You decide if you want to measure _ambient_ light (light all around or direct light) or if you want to know how much light is reflected from a surface. The amount of light measured is in the range of `0` (darkest) to `100` (brightest). + +## Parameters + +* **mode**: the type of measurement for light. This is either ``ambient`` or ``reflected`` light. + +## Returns + +* a number that is the amount of light measured. No light (darkness) is `0` and the brightest light is `100`. + +## Example + +Make the status light show ``green`` if the ambient light is greater than `20`. + +```blocks +forever(function () { + if (sensors.nxtLight1.light(NXTLightIntensityMode.Reflected) > 20) { + brick.setStatusLight(StatusLight.Green) + } else { + brick.setStatusLight(StatusLight.Orange) + } +}) +``` diff --git a/libs/nxt-light-sensor/light.ts b/libs/nxt-light-sensor/light.ts new file mode 100644 index 00000000..6f177ac8 --- /dev/null +++ b/libs/nxt-light-sensor/light.ts @@ -0,0 +1,220 @@ +const enum NXTLightSensorMode { + //% block="reflected light (raw)" + ReflectedLightRaw = 0, + //% block="reflected light" + ReflectedLight = 1, + //% block="ambient light (raw)" + AmbientLightRaw = 2, + //% block="ambient light" + AmbientLight = 3, +} + +enum NXTLightIntensityMode { + //% block="reflected light (raw)" + ReflectedRaw = NXTLightSensorMode.ReflectedLightRaw, + //% block="reflected light" + Reflected = NXTLightSensorMode.ReflectedLight, + //% block="ambient light (raw)" + //% blockHidden=true + AmbientRaw = NXTLightSensorMode.AmbientLightRaw, + //% block="ambient light" + //% blockHidden=true + Ambient = NXTLightSensorMode.AmbientLight +} + +namespace sensors { + + //% fixedInstances + export class NXTLightSensor extends internal.AnalogSensor { + + // https://github.com/mindboards/ev3sources-xtended/blob/master/ev3sources/lms2012/lms2012/Linux_AM1808/sys/settings/typedata.rcf + + private thresholdDetector: sensors.ThresholdDetector; + private darkReflectedLight: number = 3372; + private brightReflectedLight: number = 445; + private darkAmbientLight: number = 3411; + private brightAmbientLight: number = 633; + + constructor(port: number) { + super(port); + this.thresholdDetector = new sensors.ThresholdDetector(this.id()); + } + + _query() { + if (this.mode == NXTLightSensorMode.ReflectedLight) { + return [this.reflectetLight()]; + } else if (this.mode == NXTLightSensorMode.AmbientLight) { + return [this.ambientLight()]; + } else if (this.mode == NXTLightSensorMode.ReflectedLightRaw) { + return [this.reflectetLightRaw()]; + } else if (this.mode == NXTLightSensorMode.AmbientLightRaw) { + return [this.ambientLightRaw()]; + } + return [0]; + } + + _info() { + if (this.mode == NXTLightSensorMode.ReflectedLight || this.mode == NXTLightSensorMode.AmbientLight) { + return [`${this._query()[0].toString()}%`]; + } else { + return [this._query()[0].toString()]; + } + } + + _update(prev: number, curr: number) { + return this.readValue(); + } + + _deviceType() { + return DAL.DEVICE_TYPE_NXT_LIGHT; + } + + setMode(m: number) { + this._setMode(m); + } + + /** + * Gets the current light mode + */ + lightMode() { + return this.mode; + } + + // This pin is not used by the NXT Analog Sensor + _readPin6() { + return 0; + } + + /** + * Measure the ambient or reflected light value from 0 (darkest) to 100 (brightest). For raw reflection values, the range can be from 0 to 4095. + * @param sensor the color sensor port + */ + //% help=sensors/nxt-light-sensor/light + //% block="**nxt light sensor** $this|$mode" + //% blockId=nxtLight + //% parts="nxtlightsensor" + //% blockNamespace=sensors + //% this.fieldEditor="ports" + //% weight=99 blockGap=8 + //% subcategory="NXT" + //% group="Light Sensor" + light(mode: NXTLightIntensityMode) { + this.setMode(mode); + this.poke(); + switch (mode) { + case NXTLightIntensityMode.ReflectedRaw: + return this.reflectetLightRaw(); + case NXTLightIntensityMode.Reflected: + return this.reflectetLight(); + case NXTLightIntensityMode.AmbientRaw: + return this.ambientLightRaw(); + case NXTLightIntensityMode.Ambient: + return this.ambientLight(); + default: + return 0; + } + } + + /** + * Set the range of values for determining dark and light in light reflection mode. This must be done so that the reflection mode defines a value in the range from 0 to 100 percent. + * @param sensor the color sensor port + * @param dark the value of dark, eg: 0 + * @param bright the value of bright, eg: 4095 + */ + //% help=sensors/nxt-light-sensor/set-reflected-range + //% block="**nxt light sensor** $this|set reflected range dark $dark|bright $bright" + //% blockId=setReflectedLightRange + //% parts="nxtlightsensor" + //% blockNamespace=sensors + //% this.fieldEditor="ports" + //% weight=89 blockGap=8 + //% subcategory="NXT" + //% group="Light Sensor" + setReflectedLightRange(dark: number, bright: number) { + if (dark <= bright) return; + this.darkReflectedLight = Math.constrain(dark, 0, 4095); + this.brightReflectedLight = Math.constrain(bright, 0, 4095); + } + + /** + * Set the value range for dark and light detection in ambient light mode. This must be done so that the ambient light mode determines the value in the range from 0 to 100 percent. + * @param sensor the color sensor port + * @param dark the value of dark, eg: 0 + * @param bright the value of bright, eg: 4095 + */ + //% help=sensors/nxt-light-sensor/set-ambient-range + //% block="**nxt light sensor** $this|set ambient range dark $dark|bright $bright" + //% blockId=setAmbientLightRange + //% parts="nxtlightsensor" + //% blockNamespace=sensors + //% this.fieldEditor="ports" + //% weight=88 blockGap=8 + //% subcategory="NXT" + //% group="Light Sensor" + //% blockHidden=true + setAmbientLightRange(dark: number, bright: number) { + if (dark <= bright) return; + this.darkAmbientLight = Math.constrain(dark, 0, 4095); + this.brightAmbientLight = Math.constrain(bright, 0, 4095); + } + + /** + * Gets the raw light value. + */ + //% + private readValue() { + return this._readPin1(); + } + + /** + * Gets the raw reflection light value. + */ + //% + reflectetLightRaw() { + // ToDo: the red LED should be turned off in ambient lighting mode + return this.readValue(); + } + + /** + * Gets the raw ambient light value. + */ + //% + ambientLightRaw() { + // ToDo: the red LED should be turned off in ambient lighting mode + return this.readValue(); + } + + /** + * Gets the normalize reflection light value. + */ + //% + reflectetLight() { + let reflectedVal = Math.map(this.readValue(), this.darkReflectedLight, this.brightReflectedLight, 0, 100); + reflectedVal = Math.round(Math.constrain(reflectedVal, 0, 100)); + return reflectedVal; + } + + /** + * Gets the normalize ambient light value. + */ + //% + ambientLight() { + let ambientVal = Math.map(this.readValue(), this.darkAmbientLight, this.brightAmbientLight, 0, 100); + ambientVal = Math.round(Math.constrain(ambientVal, 0, 100)); + return ambientVal; + } + + } + + //% whenUsed block="1" weight=95 fixedInstance jres=icons.port1 + export const nxtLight1: NXTLightSensor = new NXTLightSensor(1); + + //% whenUsed block="2" weight=90 fixedInstance jres=icons.port2 + export const nxtLight2: NXTLightSensor = new NXTLightSensor(2); + + //% whenUsed block="3" weight=90 fixedInstance jres=icons.port3 + export const nxtLight3: NXTLightSensor = new NXTLightSensor(3); + + //% whenUsed block="4" weight=90 fixedInstance jres=icons.port4 + export const nxtLight4: NXTLightSensor = new NXTLightSensor(4); +} diff --git a/libs/nxt-light-sensor/pxt.json b/libs/nxt-light-sensor/pxt.json new file mode 100644 index 00000000..4068bd03 --- /dev/null +++ b/libs/nxt-light-sensor/pxt.json @@ -0,0 +1,15 @@ +{ + "name": "nxt-light-sensor", + "description": "NXT Light Sensor support - beta", + "files": [ + "README.md", + "light.ts" + ], + "testFiles": [ + "test.ts" + ], + "public": true, + "dependencies": { + "core": "file:../core" + } +} \ No newline at end of file diff --git a/libs/nxt-light-sensor/test.ts b/libs/nxt-light-sensor/test.ts new file mode 100644 index 00000000..27e42245 --- /dev/null +++ b/libs/nxt-light-sensor/test.ts @@ -0,0 +1,19 @@ +sensors.nxtLight1.setReflectedLightRange(2600, 1600); + +forever(function () { + control.timer1.reset(); + let nls1 = sensors.nxtLight1.reflectetLightRaw(); + let nls2 = sensors.nxtLight1.light(NXTLightIntensityMode.ReflectedRaw); + let rrlcs = sensors.color2.light(LightIntensityMode.ReflectedRaw); + let rrrcs = sensors.color3.light(LightIntensityMode.ReflectedRaw); + let t = sensors.touch4.isPressed() ? 1 : 0; + brick.clearScreen(); + brick.printValue("nls1", nls1, 1); + brick.printValue("nls2", nls2, 2); + brick.printValue("rrlcs", rrlcs, 3); + brick.printValue("rrrcs", rrrcs, 4); + brick.printValue("t", t, 5); + brick.printValue("active1", sensors.nxtLight1.isActive() ? 1 : 0, 11); + brick.printValue("active4", sensors.touch4.isActive() ? 1 : 0, 12); + control.timer1.pauseUntil(10); +}) \ No newline at end of file diff --git a/libs/touch-sensor/touch.ts b/libs/touch-sensor/touch.ts index 8c3fb979..be0fd7b5 100644 --- a/libs/touch-sensor/touch.ts +++ b/libs/touch-sensor/touch.ts @@ -12,11 +12,11 @@ namespace sensors { } _query() { - return this._readPin6() > 2500 ? [1] : [0]; + return [this._readPin6() > 2500 ? 1 : 0]; } _info() { - return this._query() ? ["pres"] : ["rel"]; + return [this._query()[0] ? "pres" : "rel"]; } _update(prev: number, curr: number) { diff --git a/pxtarget.json b/pxtarget.json index 8592b1e8..e78b80a6 100644 --- a/pxtarget.json +++ b/pxtarget.json @@ -15,6 +15,7 @@ "libs/ultrasonic-sensor", "libs/infrared-sensor", "libs/gyro-sensor", + "libs/nxt-light-sensor", "libs/screen", "libs/ev3", "libs/storage", diff --git a/sim/dalboard.ts b/sim/dalboard.ts index c778866c..de79d221 100644 --- a/sim/dalboard.ts +++ b/sim/dalboard.ts @@ -159,6 +159,7 @@ namespace pxsim { case DAL.DEVICE_TYPE_TOUCH: this.inputNodes[port] = new TouchSensorNode(port); break; case DAL.DEVICE_TYPE_ULTRASONIC: this.inputNodes[port] = new UltrasonicSensorNode(port); break; case DAL.DEVICE_TYPE_IR: this.inputNodes[port] = new InfraredSensorNode(port); break; + case DAL.DEVICE_TYPE_NXT_LIGHT: this.inputNodes[port] = new NXTLightSensorNode(port); break; } } return this.inputNodes[port]; diff --git a/sim/state/analog.ts b/sim/state/analog.ts index 7122b4f1..529312ef 100644 --- a/sim/state/analog.ts +++ b/sim/state/analog.ts @@ -1,6 +1,6 @@ namespace pxsim { - enum AnalogOff { + export enum AnalogOff { InPin1 = 0, // int16[4] InPin6 = 8, // int16[4] OutPin5 = 16, // int16[4] @@ -25,7 +25,7 @@ namespace pxsim { export class EV3AnalogState { constructor() { - let data = new Uint8Array(5172) + let data = new Uint8Array(5172); MMapMethods.register("/dev/lms_analog", { data, beforeMemRead: () => { @@ -36,24 +36,34 @@ namespace pxsim { for (let port = 0; port < DAL.NUM_INPUTS; port++) { const node = inputNodes[port]; if (node) { - data[AnalogOff.InConn + port] = node.isUart() ? DAL.CONN_INPUT_UART : DAL.CONN_INPUT_DUMB; + if (node.isAnalog()) data[AnalogOff.InDcm + port] = node.getDeviceType(); + data[AnalogOff.InConn + port] = node.isUart() ? DAL.CONN_INPUT_UART : (!node.isNXT() ? DAL.CONN_INPUT_DUMB : DAL.CONN_NXT_DUMB); if (node.isAnalog() && node.hasData()) { //data[AnalogOff.InPin6 + 2 * port] = node.getValue(); - util.map16Bit(data, AnalogOff.InPin6 + 2 * port, Math.floor(node.getValue())); + util.map16Bit(data, node.getAnalogReadPin() + 2 * port, Math.floor(node.getValue())); } } } }, read: buf => { - let v = "vSIM" + let v = "vSIM"; for (let i = 0; i < buf.data.length; ++i) - buf.data[i] = v.charCodeAt(i) || 0 - return buf.data.length + buf.data[i] = v.charCodeAt(i) || 0; + return buf.data.length; }, write: buf => { - return 2 + return 2; }, ioctl: (id, buf) => { + //console.log("ioctl: " + id); + for (let port = 0; port < DAL.NUM_INPUTS; port++) { + const connection = buf.data[DevConOff.Connection + port]; + const type = buf.data[DevConOff.Type + port]; + const mode = buf.data[DevConOff.Mode + port]; + //console.log(`${port}, mode: ${mode}`); + const node = ev3board().getInputNodes()[port]; + if (node) node.setMode(mode); + } return 2; } }) diff --git a/sim/state/nodeTypes.ts b/sim/state/nodeTypes.ts index 53d4c9c7..255353cd 100644 --- a/sim/state/nodeTypes.ts +++ b/sim/state/nodeTypes.ts @@ -8,7 +8,8 @@ namespace pxsim { GyroSensor = 5, ColorSensor = 6, UltrasonicSensor = 7, - InfraredSensor = 8 + InfraredSensor = 8, + NXTLightSensor = 9 } export interface Node { diff --git a/sim/state/nxtlight.ts b/sim/state/nxtlight.ts new file mode 100644 index 00000000..fce1d9d7 --- /dev/null +++ b/sim/state/nxtlight.ts @@ -0,0 +1,58 @@ +/// + +namespace pxsim { + + export enum NXTLightSensorMode { + None = -1, + ReflectedLightRaw = 0, + ReflectedLight = 1, + AmbientLightRaw = 2, + AmbientLight = 3, + } + + export class NXTLightSensorNode extends AnalogSensorNode { + + id = NodeType.NXTLightSensor; + + private value: number = 0; + public darkReflectedLight: number = 3372; + public brightReflectedLight: number = 445; + public darkAmbientLight: number = 3411; + public brightAmbientLight: number = 633; + + constructor(port: number) { + super(port); + this.mode = -1; + } + + getDeviceType() { + return DAL.DEVICE_TYPE_NXT_LIGHT; + } + + setValue(value: number) { + this.value = value; + this.setChangedState(); + } + + getValue() { + return this.value; + } + + setMode(mode: number) { + this.mode = mode; + if (this.mode == NXTLightSensorMode.ReflectedLight) this.value = 1908; + else if (this.mode == NXTLightSensorMode.AmbientLight) this.value = 2022; + else this.value = 2048; + this.changed = true; + this.modeChanged = true; + } + + getAnalogReadPin() { + return AnalogOff.InPin1; + } + + isNXT() { + return true; + } + } +} \ No newline at end of file diff --git a/sim/state/sensor.ts b/sim/state/sensor.ts index 42e2df01..3aa75659 100644 --- a/sim/state/sensor.ts +++ b/sim/state/sensor.ts @@ -1,4 +1,3 @@ - namespace pxsim { export class SensorNode extends BaseNode { @@ -20,6 +19,10 @@ namespace pxsim { return false; } + public isNXT() { + return false; + } + public isModeReturnArr() { return this.modeReturnArr; } @@ -32,6 +35,10 @@ namespace pxsim { return [0]; } + public getAnalogReadPin() { + return AnalogOff.InPin6; // Defl for ev3 sensor + } + setMode(mode: number) { this.mode = mode; this.changed = true; diff --git a/sim/state/uart.ts b/sim/state/uart.ts index 820097a8..c9a99683 100644 --- a/sim/state/uart.ts +++ b/sim/state/uart.ts @@ -39,7 +39,7 @@ namespace pxsim { } - enum DevConOff { + export enum DevConOff { Connection = 0, // int8[4] Type = 4, // int8[4] Mode = 8, // int8[4] @@ -106,13 +106,13 @@ namespace pxsim { } }, read: buf => { - let v = "vSIM" + let v = "vSIM"; // for (let i = 0; i < buf.data.length; ++i) // buf.data[i] = v.charCodeAt(i) || 0 - return buf.data.length + return buf.data.length; }, write: buf => { - return 2 + return 2; }, ioctl: (id, buf) => { switch (id) { diff --git a/sim/visuals/assets/NXT Light Sensor.svg b/sim/visuals/assets/NXT Light Sensor.svg new file mode 100644 index 00000000..f39772bd --- /dev/null +++ b/sim/visuals/assets/NXT Light Sensor.svg @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sim/visuals/assets/NXT Light Sensor.ts b/sim/visuals/assets/NXT Light Sensor.ts new file mode 100644 index 00000000..610c2ab2 --- /dev/null +++ b/sim/visuals/assets/NXT Light Sensor.ts @@ -0,0 +1,150 @@ +namespace pxsim { + export const NXT_LIGHT_SENSOR_SVG = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `; +} \ No newline at end of file diff --git a/sim/visuals/board.ts b/sim/visuals/board.ts index 69d3e952..541bd25a 100644 --- a/sim/visuals/board.ts +++ b/sim/visuals/board.ts @@ -268,6 +268,13 @@ namespace pxsim.visuals { view = new RotationSliderControl(this.element, this.defs, state, port); break; } + case NodeType.NXTLightSensor: { + const state = ev3board().getInputNodes()[port] as NXTLightSensorNode; + if (state.getMode() != NXTLightSensorMode.None) { + view = new LightWheelControl(this.element, this.defs, state, port); + } + break; + } case NodeType.MediumMotor: case NodeType.LargeMotor: { const state = ev3board().getMotors()[port]; @@ -306,6 +313,8 @@ namespace pxsim.visuals { view = new UltrasonicSensorView(port); break; case NodeType.InfraredSensor: view = new InfraredView(port); break; + case NodeType.NXTLightSensor: + view = new NXTLightSensorView(port); break; case NodeType.Brick: //return new BrickView(0); view = this.layoutView.getBrick(); break; diff --git a/sim/visuals/controls/lightWheel.ts b/sim/visuals/controls/lightWheel.ts new file mode 100644 index 00000000..a2c3585e --- /dev/null +++ b/sim/visuals/controls/lightWheel.ts @@ -0,0 +1,129 @@ +namespace pxsim.visuals { + + export class LightWheelControl extends ControlView { + + private group: SVGGElement; + private colorGradient: SVGLinearGradientElement; + private reporter: SVGTextElement; + private rect: SVGElement; + + getInnerWidth() { + return 111; + } + + getInnerHeight() { + return 192; + } + + private getReporterHeight() { + return 38; + } + + private getSliderWidth() { + return 62; + } + + private getSliderHeight() { + return 131; + } + + private getMinValue(state: NXTLightSensorNode) { + if (state.getMode() == NXTLightSensorMode.ReflectedLight) return state.brightReflectedLight; + else if (state.getMode() == NXTLightSensorMode.AmbientLight) return state.brightAmbientLight; + return 0; + } + + private getMaxValue(state: NXTLightSensorNode) { + if (state.getMode() == NXTLightSensorMode.ReflectedLightRaw || state.getMode() == NXTLightSensorMode.AmbientLightRaw) { + return 4095; + } else if (state.getMode() == NXTLightSensorMode.ReflectedLight) { + return state.darkReflectedLight; + } else if (state.getMode() == NXTLightSensorMode.AmbientLight) { + return state.darkAmbientLight; + } + return 100; + } + + private mapValue(x: number, inMin: number, inMax: number, outMin: number, outMax: number) { + return (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; + } + + updateState() { + if (!this.visible) { + return; + } + const node = this.state; + const value = node.getValue(); + let inverseValue = this.getMaxValue(node) - value + this.getMinValue(node); + if (node.getMode() == NXTLightSensorMode.ReflectedLightRaw || node.getMode() == NXTLightSensorMode.AmbientLightRaw) { + inverseValue = this.mapValue(inverseValue, 0, 4095, 0, 100); + } else if (node.getMode() == NXTLightSensorMode.ReflectedLight) { + inverseValue = this.mapValue(inverseValue, node.darkReflectedLight, node.brightReflectedLight, 0, 100); + } else if (node.getMode() == NXTLightSensorMode.AmbientLight) { + inverseValue = this.mapValue(inverseValue, node.darkAmbientLight, node.brightAmbientLight, 0, 100); + } + svg.setGradientValue(this.colorGradient, inverseValue + "%"); + if (node.getMode() == NXTLightSensorMode.ReflectedLightRaw || node.getMode() == NXTLightSensorMode.AmbientLightRaw) { + this.reporter.textContent = `${Math.floor(parseFloat(value.toString()))}`; + } else { + this.reporter.textContent = `${Math.floor(this.mapValue(parseFloat(value.toString()), this.getMaxValue(node), this.getMinValue(node), 0, 100))}%`; + } + } + + updateColorLevel(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) { + const state = this.state; + let cur = svg.cursorPoint(pt, parent, ev); + const bBox = this.rect.getBoundingClientRect(); + const height = bBox.height; + let t = Math.max(0, Math.min(1, (height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height)); + if (state.getMode() == NXTLightSensorMode.ReflectedLight || state.getMode() == NXTLightSensorMode.AmbientLight) t = 1 - t; + state.setValue(this.getMinValue(state) + t * (this.getMaxValue(state) - this.getMinValue(state))); + } + + getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) { + this.group = svg.elt("g") as SVGGElement; + + let gc = "gradient-color-" + this.getPort(); + const prevColorGradient = globalDefs.querySelector(`#${gc}`) as SVGLinearGradientElement; + this.colorGradient = prevColorGradient ? prevColorGradient : svg.linearGradient(globalDefs, gc, false); + svg.setGradientValue(this.colorGradient, "50%"); + svg.setGradientColors(this.colorGradient, "black", "yellow"); + + const reporterGroup = pxsim.svg.child(this.group, "g"); + reporterGroup.setAttribute("transform", `translate(${this.getWidth() / 2}, 20)`); + this.reporter = pxsim.svg.child(reporterGroup, "text", { 'text-anchor': 'middle', 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement; + + const sliderGroup = pxsim.svg.child(this.group, "g"); + sliderGroup.setAttribute("transform", `translate(${this.getWidth() / 2 - this.getSliderWidth() / 2}, ${this.getReporterHeight()})`); + + const rect = pxsim.svg.child(sliderGroup, "rect", + { + "width": this.getSliderWidth(), + "height": this.getSliderHeight(), + "style": `fill: url(#${gc})` + } + ) + this.rect = rect; + + let pt = parent.createSVGPoint(); + let captured = false; + touchEvents(rect, ev => { + if (captured && (ev as MouseEvent).clientY) { + ev.preventDefault(); + this.updateColorLevel(pt, parent, ev as MouseEvent); + } + }, ev => { + captured = true; + if ((ev as MouseEvent).clientY) { + rect.setAttribute('cursor', '-webkit-grabbing'); + this.updateColorLevel(pt, parent, ev as MouseEvent); + } + }, () => { + captured = false; + rect.setAttribute('cursor', '-webkit-grab'); + }) + + return this.group; + } + } +} \ No newline at end of file diff --git a/sim/visuals/nodes/nxtLightSensorView.ts b/sim/visuals/nodes/nxtLightSensorView.ts new file mode 100644 index 00000000..890815f0 --- /dev/null +++ b/sim/visuals/nodes/nxtLightSensorView.ts @@ -0,0 +1,42 @@ +/// + +namespace pxsim.visuals { + export class NXTLightSensorView extends SensorView implements LayoutElement { + + private control: LightWheelControl; + + private static sensor_hole_id = 'led'; + + constructor(port: number) { + super(NXT_LIGHT_SENSOR_SVG, "color", NodeType.NXTLightSensor, port); + } + + protected optimizeForLightMode() { + (this.content.getElementById(this.normalizeId('box')) as SVGElement).style.fill = '#a8aaa8'; + } + + public getPaddingRatio() { + return 1 / 4; + } + + public updateState() { + super.updateState(); + + const lightState = ev3board().getInputNodes()[this.port]; + if (!lightState) return; + const mode = lightState.getMode(); + + if (mode == NXTLightSensorMode.ReflectedLightRaw || mode == NXTLightSensorMode.ReflectedLight) { + this.updateSensorLightVisual('#eb0c0c'); + } + } + + private updateSensorLightVisual(color: string) { + const sensorHole = this.content.getElementById(this.normalizeId(NXTLightSensorView.sensor_hole_id)) as SVGCircleElement; + sensorHole.style.stroke = color; + if (color != '#ffffff') { + sensorHole.style.strokeWidth = '2px'; + } + } + } +} \ No newline at end of file From 2c8d3d5ed264a9e59529790bdfb92961f6e38240 Mon Sep 17 00:00:00 2001 From: Joey Wunderlich Date: Fri, 2 Feb 2024 07:33:43 -0800 Subject: [PATCH 2/2] 1.4.38 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index daa1c029..3ea2a01e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pxt-ev3", - "version": "1.4.37", + "version": "1.4.38", "description": "LEGO MINDSTORMS EV3 for Microsoft MakeCode", "private": false, "keywords": [