pxt-ev3/libs/core/input.ts

488 lines
15 KiB
TypeScript
Raw Normal View History

2017-10-28 18:13:02 +02:00
namespace sensors.internal {
2017-07-08 12:35:23 +02:00
//% shim=pxt::unsafePollForChanges
2017-07-10 15:16:31 +02:00
export function unsafePollForChanges(
2017-07-11 11:40:40 +02:00
periodMs: number,
query: () => number,
changeHandler: (prev: number, curr: number) => void
) {
// This is implemented in C++ without blocking the regular JS when query() is runnning
// which is generally unsafe. Query should not update globally visible state, and cannot
// call any yielding functions, like sleep().
// This is implementation for the simulator.
control.runInBackground(() => {
let prev = query()
changeHandler(prev, prev)
2017-07-11 11:40:40 +02:00
while (true) {
loops.pause(periodMs)
let curr = query()
if (prev !== curr) {
changeHandler(prev, curr)
prev = curr
}
}
})
}
2017-07-08 12:35:23 +02:00
2017-07-08 12:16:12 +02:00
let analogMM: MMap
2017-07-10 12:29:42 +02:00
let uartMM: MMap
let devcon: Buffer
2017-10-24 20:58:52 +02:00
let sensorInfos: SensorInfo[]
2017-07-08 12:16:12 +02:00
2017-07-10 12:29:42 +02:00
class SensorInfo {
port: number
sensor: Sensor
2017-10-24 20:58:52 +02:00
sensors: Sensor[]
2017-07-10 12:29:42 +02:00
connType: number
devType: number
constructor(p: number) {
this.port = p
this.connType = DAL.CONN_NONE
this.devType = DAL.DEVICE_TYPE_NONE
2017-10-24 20:58:52 +02:00
this.sensors = []
2017-07-10 12:29:42 +02:00
}
2017-07-08 20:17:36 +02:00
}
2017-07-08 12:16:12 +02:00
function init() {
2017-10-24 20:58:52 +02:00
if (sensorInfos) return
sensorInfos = []
for (let i = 0; i < DAL.NUM_INPUTS; ++i) sensorInfos.push(new SensorInfo(i))
2017-07-10 12:29:42 +02:00
devcon = output.createBuffer(DevConOff.Size)
2017-07-08 20:17:36 +02:00
analogMM = control.mmap("/dev/lms_analog", AnalogOff.Size, 0)
2017-07-08 12:16:12 +02:00
if (!analogMM) control.fail("no analog sensor")
2017-07-10 12:29:42 +02:00
uartMM = control.mmap("/dev/lms_uart", UartOff.Size, 0)
if (!uartMM) control.fail("no uart sensor")
loops.forever(() => {
detectDevices()
loops.pause(500)
})
2017-10-24 20:58:52 +02:00
for (let info_ of sensorInfos) {
2017-07-10 12:29:42 +02:00
let info = info_
unsafePollForChanges(50, () => {
if (info.sensor) return info.sensor._query()
return 0
}, (prev, curr) => {
if (info.sensor) info.sensor._update(prev, curr)
})
}
2017-07-08 12:16:12 +02:00
}
export function getActiveSensors(): Sensor[] {
init();
return sensorInfos.filter(si => si.sensor && si.sensor.isActive()).map(si => si.sensor);
}
2017-07-10 12:29:42 +02:00
function readUartInfo(port: number, mode: number) {
let buf = output.createBuffer(UartCtlOff.Size)
buf[UartCtlOff.Port] = port
buf[UartCtlOff.Mode] = mode
uartMM.ioctl(IO.UART_READ_MODE_INFO, buf)
return buf
//let info = `t:${buf[TypesOff.Type]} c:${buf[TypesOff.Connection]} m:${buf[TypesOff.Mode]} n:${buf.slice(0, 12).toHex()}`
//serial.writeLine("UART " + port + " / " + mode + " - " + info)
2017-07-09 19:12:24 +02:00
}
2017-07-10 12:29:42 +02:00
function detectDevices() {
let conns = analogMM.slice(AnalogOff.InConn, DAL.NUM_INPUTS)
2017-07-10 12:29:42 +02:00
let numChanged = 0
2017-10-24 20:58:52 +02:00
for (let info of sensorInfos) {
2017-07-10 12:29:42 +02:00
let newConn = conns[info.port]
if (newConn == info.connType)
continue
numChanged++
info.connType = newConn
info.devType = DAL.DEVICE_TYPE_NONE
if (newConn == DAL.CONN_INPUT_UART) {
2017-07-10 13:47:00 +02:00
control.dmesg(`new UART connection at ${info.port}`)
2017-07-10 12:29:42 +02:00
setUartMode(info.port, 0)
let uinfo = readUartInfo(info.port, 0)
info.devType = uinfo[TypesOff.Type]
2017-07-10 13:47:00 +02:00
control.dmesg(`UART type ${info.devType}`)
} else if (newConn == DAL.CONN_INPUT_DUMB) {
2017-07-10 13:47:00 +02:00
control.dmesg(`new DUMB connection at ${info.port}`)
2017-07-10 12:29:42 +02:00
// TODO? for now assume touch
info.devType = DAL.DEVICE_TYPE_TOUCH
} else if (newConn == DAL.CONN_NONE || newConn == 0) {
2017-07-10 13:47:00 +02:00
control.dmesg(`disconnect at ${info.port}`)
2017-07-10 12:29:42 +02:00
} else {
2017-07-10 13:47:00 +02:00
control.dmesg(`unknown connection type: ${newConn} at ${info.port}`)
2017-07-10 12:29:42 +02:00
}
}
2017-07-08 20:17:36 +02:00
2017-07-10 12:29:42 +02:00
if (numChanged == 0)
return
2017-10-24 20:58:52 +02:00
for (let si of sensorInfos) {
if (si.sensor && si.sensor._deviceType() != si.devType) {
si.sensor = null
}
if (si.devType != DAL.DEVICE_TYPE_NONE) {
2017-10-27 20:18:56 +02:00
// TODO figure out compiler problem when '|| null' is added here!
si.sensor = si.sensors.filter(s => s._deviceType() == si.devType)[0]
2017-10-24 20:58:52 +02:00
if (si.sensor == null) {
control.dmesg(`sensor not found for type=${si.devType} at ${si.port}`)
} else {
control.dmesg(`sensor connected type=${si.devType} at ${si.port}`)
si.sensor._activated()
2017-07-10 12:29:42 +02:00
}
}
2017-07-08 20:17:36 +02:00
}
2017-07-10 12:29:42 +02:00
}
2017-07-10 12:42:30 +02:00
export class Sensor extends control.Component {
2017-11-29 01:02:04 +01:00
protected _port: number // this is 0-based
2017-07-08 12:35:23 +02:00
2017-10-24 20:58:52 +02:00
constructor(port_: number) {
2017-07-10 12:29:42 +02:00
super()
2017-10-24 20:58:52 +02:00
if (!(1 <= port_ && port_ <= DAL.NUM_INPUTS))
control.panic(120)
2017-11-29 01:02:04 +01:00
this._port = port_ - 1
2017-07-08 12:35:23 +02:00
init()
2017-11-29 01:02:04 +01:00
sensorInfos[this._port].sensors.push(this)
2017-12-18 22:04:17 +01:00
this.markUsed();
}
markUsed() {
sensors.__sensorUsed(this._port, this._deviceType());
2017-07-10 12:29:42 +02:00
}
2017-10-24 20:58:52 +02:00
_activated() { }
2017-07-10 12:29:42 +02:00
2017-10-24 20:58:52 +02:00
// 1-based
2017-11-29 01:02:04 +01:00
port() {
return this._port + 1
2017-07-10 12:29:42 +02:00
}
2017-10-24 20:58:52 +02:00
isActive() {
2017-11-29 01:02:04 +01:00
return sensorInfos[this._port].sensor == this
2017-07-10 12:29:42 +02:00
}
_query() {
return 0
}
_update(prev: number, curr: number) {
}
_deviceType() {
return 0
2017-07-08 20:17:36 +02:00
}
}
2017-07-10 12:29:42 +02:00
export class AnalogSensor extends Sensor {
2017-10-24 20:58:52 +02:00
constructor(port: number) {
super(port)
2017-07-10 12:29:42 +02:00
}
2017-07-08 20:17:36 +02:00
2017-07-10 12:29:42 +02:00
_readPin6() {
2017-10-24 20:58:52 +02:00
if (!this.isActive()) return 0
2017-11-29 01:02:04 +01:00
return analogMM.getNumber(NumberFormat.Int16LE, AnalogOff.InPin6 + 2 * this._port)
2017-07-08 12:35:23 +02:00
}
2017-07-10 12:29:42 +02:00
}
2017-07-08 12:35:23 +02:00
2017-11-30 07:47:17 +01:00
export enum ThresholdState {
Normal = 1,
High = 2,
Low = 3,
}
export class ThresholdDetector {
2017-11-30 07:47:17 +01:00
public id: number;
private min: number;
private max: number;
private lowThreshold: number;
private highThreshold: number;
private level: number;
public state: ThresholdState;
2017-11-30 07:47:17 +01:00
constructor(id: number, min = 0, max = 100, lowThreshold = 20, highThreshold = 80) {
this.id = id;
this.min = min;
this.max = max;
this.lowThreshold = lowThreshold;
this.highThreshold = highThreshold;
this.level = Math.ceil((max - min) / 2);
this.state = ThresholdState.Normal;
}
public setLevel(level: number) {
this.level = this.clampValue(level);
if (this.level >= this.highThreshold) {
this.setState(ThresholdState.High);
}
else if (this.level <= this.lowThreshold) {
this.setState(ThresholdState.Low);
}
else {
this.setState(ThresholdState.Normal);
}
}
public setLowThreshold(value: number) {
this.lowThreshold = this.clampValue(value);
this.highThreshold = Math.max(this.lowThreshold + 1, this.highThreshold);
}
public setHighThreshold(value: number) {
this.highThreshold = this.clampValue(value);
this.lowThreshold = Math.min(this.highThreshold - 1, this.lowThreshold);
}
private clampValue(value: number) {
if (value < this.min) {
return this.min;
}
else if (value > this.max) {
return this.max;
}
return value;
}
2017-07-10 12:29:42 +02:00
2017-11-30 07:47:17 +01:00
private setState(state: ThresholdState) {
if (this.state == state) return;
this.state = state;
switch (state) {
case ThresholdState.High:
control.raiseEvent(this.id, ThresholdState.High);
break;
case ThresholdState.Low:
control.raiseEvent(this.id, ThresholdState.Low);
break;
case ThresholdState.Normal:
break;
}
}
}
2017-07-10 12:29:42 +02:00
export class UartSensor extends Sensor {
2017-10-24 20:58:52 +02:00
protected mode: number // the mode user asked for
protected realmode: number // the mode the hardware is in
2017-07-10 12:29:42 +02:00
2017-10-24 20:58:52 +02:00
constructor(port: number) {
super(port)
2017-07-10 13:47:00 +02:00
this.mode = 0
this.realmode = -1
2017-07-08 12:35:23 +02:00
}
2017-10-24 20:58:52 +02:00
_activated() {
this.realmode = 0
// uartReset(this.port) // TODO is it ever needed?
this._setMode(this.mode)
2017-07-10 12:29:42 +02:00
}
protected _setMode(m: number) {
2017-07-10 13:47:00 +02:00
//control.dmesg(`_setMode p=${this.port} m: ${this.realmode} -> ${m}`)
2017-07-10 12:29:42 +02:00
let v = m | 0
2017-07-10 13:47:00 +02:00
this.mode = v
2017-10-24 20:58:52 +02:00
if (!this.isActive()) return
2017-07-10 13:47:00 +02:00
if (this.realmode != this.mode) {
this.realmode = v
2017-11-29 01:02:04 +01:00
setUartMode(this._port, v)
2017-07-10 12:29:42 +02:00
}
}
getBytes(): Buffer {
2017-11-29 01:02:04 +01:00
return getUartBytes(this.isActive() ? this._port : -1)
2017-07-10 12:29:42 +02:00
}
getNumber(fmt: NumberFormat, off: number) {
2017-10-24 20:58:52 +02:00
if (!this.isActive())
return 0
2017-11-29 01:02:04 +01:00
return getUartNumber(fmt, off, this._port)
2017-07-08 12:35:23 +02:00
}
}
2017-07-10 12:29:42 +02:00
function uartReset(port: number) {
if (port < 0) return
2017-07-10 13:47:00 +02:00
control.dmesg(`UART reset at ${port}`)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Connection + port, DAL.CONN_NONE)
2017-07-10 12:29:42 +02:00
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Type + port, 0)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Mode + port, 0)
uartMM.ioctl(IO.UART_SET_CONN, devcon)
2017-07-08 12:16:12 +02:00
}
2017-07-10 12:29:42 +02:00
function getUartStatus(port: number) {
if (port < 0) return 0
return uartMM.getNumber(NumberFormat.Int8LE, UartOff.Status + port)
}
2017-07-08 12:16:12 +02:00
2017-07-10 12:29:42 +02:00
function waitNonZeroUartStatus(port: number) {
while (true) {
if (port < 0) return 0
let s = getUartStatus(port)
if (s) return s
loops.pause(25)
}
}
2017-07-08 20:17:36 +02:00
2017-07-10 12:29:42 +02:00
function uartClearChange(port: number) {
const UART_DATA_READY = 8
const UART_PORT_CHANGED = 1
while (true) {
let status = getUartStatus(port)
if (port < 0) break
2017-07-08 20:17:36 +02:00
2017-07-10 12:29:42 +02:00
if ((status & UART_DATA_READY) != 0 && (status & UART_PORT_CHANGED) == 0)
break
2017-07-08 20:17:36 +02:00
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Connection + port, DAL.CONN_INPUT_UART)
2017-07-10 12:29:42 +02:00
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Type + port, 0)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Mode + port, 0)
2017-07-08 20:17:36 +02:00
2017-07-10 12:29:42 +02:00
uartMM.ioctl(IO.UART_CLEAR_CHANGED, devcon)
uartMM.setNumber(NumberFormat.Int8LE, UartOff.Status + port,
getUartStatus(port) & 0xfffe)
loops.pause(10)
}
}
function setUartMode(port: number, mode: number) {
const UART_PORT_CHANGED = 1
while (true) {
if (port < 0) return
2017-07-10 13:47:00 +02:00
control.dmesg(`UART set mode to ${mode} at ${port}`)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Connection + port, DAL.CONN_INPUT_UART)
2017-07-10 12:29:42 +02:00
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Type + port, 33)
devcon.setNumber(NumberFormat.Int8LE, DevConOff.Mode + port, mode)
uartMM.ioctl(IO.UART_SET_CONN, devcon)
let status = waitNonZeroUartStatus(port)
if (status & UART_PORT_CHANGED) {
uartClearChange(port)
} else {
break
}
loops.pause(10)
}
}
function getUartBytes(port: number): Buffer {
if (port < 0) return output.createBuffer(DAL.MAX_DEVICE_DATALENGTH)
2017-07-10 12:29:42 +02:00
let index = uartMM.getNumber(NumberFormat.UInt16LE, UartOff.Actual + port * 2)
return uartMM.slice(
UartOff.Raw + DAL.MAX_DEVICE_DATALENGTH * 300 * port + DAL.MAX_DEVICE_DATALENGTH * index,
DAL.MAX_DEVICE_DATALENGTH)
2017-07-10 12:29:42 +02:00
}
function getUartNumber(fmt: NumberFormat, off: number, port: number) {
if (port < 0) return 0
let 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)
2017-07-10 12:29:42 +02:00
}
const enum NxtColOff {
Calibration = 0, // uint32[4][3]
CalLimits = 48, // uint16[2]
Crc = 52, // uint16
ADRaw = 54, // uint16[4]
SensorRaw = 62, // uint16[4]
Padding = 70,
Size = 72
}
const enum AnalogOff {
InPin1 = 0, // int16[4]
InPin6 = 8, // int16[4]
OutPin5 = 16, // int16[4]
BatteryTemp = 24, // int16
MotorCurrent = 26, // int16
BatteryCurrent = 28, // int16
Cell123456 = 30, // int16
Pin1 = 32, // int16[300][4]
Pin6 = 2432, // int16[300][4]
Actual = 4832, // uint16[4]
LogIn = 4840, // uint16[4]
LogOut = 4848, // uint16[4]
NxtCol = 4856, // uint16[36][4] - NxtColor*4
OutPin5Low = 5144, // int16[4]
Updated = 5152, // int8[4]
InDcm = 5156, // int8[4]
InConn = 5160, // int8[4]
OutDcm = 5164, // int8[4]
OutConn = 5168, // int8[4]
Size = 5172
}
2017-07-08 12:16:12 +02:00
2017-07-08 20:17:36 +02:00
const enum DevConOff {
Connection = 0, // int8[4]
Type = 4, // int8[4]
Mode = 8, // int8[4]
Size = 12
}
const enum TypesOff {
Name = 0, // int8[12]
Type = 12, // int8
Connection = 13, // int8
Mode = 14, // int8
DataSets = 15, // int8
Format = 16, // int8
Figures = 17, // int8
Decimals = 18, // int8
Views = 19, // int8
RawMin = 20, // float32
RawMax = 24, // float32
PctMin = 28, // float32
PctMax = 32, // float32
SiMin = 36, // float32
SiMax = 40, // float32
InvalidTime = 44, // uint16
IdValue = 46, // uint16
Pins = 48, // int8
Symbol = 49, // int8[5]
Align = 54, // uint16
Size = 56
}
const enum UartOff {
TypeData = 0, // Types[8][4]
Repeat = 1792, // uint16[300][4]
Raw = 4192, // int8[32][300][4]
Actual = 42592, // uint16[4]
LogIn = 42600, // uint16[4]
Status = 42608, // int8[4]
Output = 42612, // int8[32][4]
OutputLength = 42740, // int8[4]
Size = 42744
}
const enum UartCtlOff {
TypeData = 0, // Types
Port = 56, // int8
Mode = 57, // int8
Size = 58
}
const enum IO {
UART_SET_CONN = 0xc00c7500,
UART_READ_MODE_INFO = 0xc03c7501,
UART_NACK_MODE_INFO = 0xc03c7502,
UART_CLEAR_CHANGED = 0xc03c7503,
IIC_SET_CONN = 0xc00c6902,
IIC_READ_TYPE_INFO = 0xc03c6903,
IIC_SETUP = 0xc04c6905,
IIC_SET = 0xc02c6906,
TST_PIN_ON = 0xc00b7401,
TST_PIN_OFF = 0xc00b7402,
TST_PIN_READ = 0xc00b7403,
TST_PIN_WRITE = 0xc00b7404,
TST_UART_ON = 0xc0487405,
TST_UART_OFF = 0xc0487406,
TST_UART_EN = 0xc0487407,
TST_UART_DIS = 0xc0487408,
TST_UART_READ = 0xc0487409,
TST_UART_WRITE = 0xc048740a,
}
2017-07-10 12:29:42 +02:00
}