diff --git a/docs/robot/lekce1/example-gridui/@types/adc.d.ts b/docs/robot/lekce1/example-gridui/@types/adc.d.ts new file mode 100644 index 00000000..871de201 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/adc.d.ts @@ -0,0 +1,24 @@ +declare module "adc" { + type Atten = number; + + const Attenuation: { + readonly Db0: Atten; + readonly Db2_5: Atten; + readonly Db6: Atten; + readonly Db11: Atten; + }; + + + /** + * Enable ADC on the given pin. + * @param pin The pin to enable ADC on. + */ + function configure(pin: number, attenuation?: Atten): void; + + /** + * Read the value of the given pin. + * @param pin The pin to read. + * @returns The value of the pin (0-1023) + */ + function read(pin: number): number; +} diff --git a/docs/robot/lekce1/example-gridui/@types/basicStream.d.ts b/docs/robot/lekce1/example-gridui/@types/basicStream.d.ts new file mode 100644 index 00000000..70c9e06f --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/basicStream.d.ts @@ -0,0 +1,22 @@ +declare interface Writable { + /** + * Write the given data to the stream. + * @param data The data to write. + */ + write(data: string): void; +} + +declare interface Readable { + /** + * Read a single character from the stream. + * @returns Promise that resolves to the character read. + */ + get(): Promise; + + /** + * Read a chunk of data from the stream. The size of the chunk is + * given by the implementation and available data. + * @returns Promise that resolves to the data read. + */ + read(): Promise; +} diff --git a/docs/robot/lekce1/example-gridui/@types/fs.d.ts b/docs/robot/lekce1/example-gridui/@types/fs.d.ts new file mode 100644 index 00000000..a21dc650 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/fs.d.ts @@ -0,0 +1,80 @@ +declare module "fs" { + interface File { + path: string; + + /** + * Check if the file is open. + */ + isOpen(): boolean; + + /** + * Close the file. + */ + close(): void; + + /** + * Read characters from the file. + * @param len The number of characters to read. + */ + read(len: number): string; + + /** + * Write text to the file. + * @param text The text to write. + */ + write(text: string): void; + } + + /** + * Open the given file in the given mode. + * @param path The path to the file. + * @param mode The mode to open the file in ("r", "w", "a" and combinations). + */ + function open(path: string, mode: string): File; + + /** + * Check if the given path exists. + * @param path The path to check. + * @returns True if the path exists, false otherwise. + */ + function exists(path: string): boolean; + + /** + * Check if the given path is a file. + * @param path The path to check. + * @returns True if the path is a file, false otherwise. + */ + function isFile(path: string): boolean; + + /** + * Check if the given path is a directory. + * @param path The path to check. + * @returns True if the path is a directory, false otherwise. + */ + function isDirectory(path: string): boolean; + + /** + * Create a directory at the given path. + * @param path The path to create the directory at. + */ + function mkdir(path: string): void; + + /** + * Remove the file at the given path. + * @param path The path to the file to remove. + */ + function rm(path: string): void; + + /** + * Remove the directory at the given path. + * @param path The path to the directory to remove. + */ + function rmdir(path: string): void; + + /** + * List the files in the given directory. + * @param path The path to the directory to list. + * @returns An array of file names in the directory. + */ + function readdir(path: string): string[]; +} diff --git a/docs/robot/lekce1/example-gridui/@types/gpio.d.ts b/docs/robot/lekce1/example-gridui/@types/gpio.d.ts new file mode 100644 index 00000000..09c167db --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/gpio.d.ts @@ -0,0 +1,49 @@ +declare module "gpio" { + const PinMode: { + readonly DISABLE: number, + readonly OUTPUT: number, + readonly INPUT: number, + readonly INPUT_PULLUP: number, + readonly INPUT_PULLDOWN: number, + }; + + interface EventInfo { + timestamp: Timestamp; + } + + /** + * Configure the given pin. + * @param pin The pin to configure. + * @param mode The mode to configure the pin in. + */ + function pinMode(pin: number, mode: number): void; + + /** + * Write digital value to the given pin. + * @param pin The pin to write to. + * @param value The value to write. + */ + function write(pin: number, value: number): void; + + /** + * Read digital value from the given pin. + * @param pin The pin to read from. + * @returns The value of the pin (0 or 1). + */ + function read(pin: number): number; + + /** + * Set event handler for the given pin. + * @param event The event to handle. + * @param pin The pin to handle the event for. + * @param callback The callback to call when the event occurs. + */ + function on(event: "rising" | "falling" | "change", pin: number, callback: (info: EventInfo) => void): void; + + /** + * Remove event handler for the given pin. + * @param event The event to remove. + * @param pin The pin to remove the event handler for. + */ + function off(event: "rising" | "falling" | "change", pin: number): void; +} diff --git a/docs/robot/lekce1/example-gridui/@types/gridui.d.ts b/docs/robot/lekce1/example-gridui/@types/gridui.d.ts new file mode 100644 index 00000000..07e6bbe0 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/gridui.d.ts @@ -0,0 +1,321 @@ +declare module "gridui" { + namespace widget { + interface Base { + readonly uuid: number + widgetX: number + widgetY: number + widgetW: number + widgetH: number + widgetTab: number + css(key: string): string + setCss(key: string, value: string): void + } + + interface Arm extends Base { + readonly x: number + readonly y: number + } + + interface Bar extends Base { + color: string + fontSize: number + min: number + max: number + value: number + showValue: boolean + } + + interface Button extends Base { + text: string + fontSize: number + color: string + background: string + align: string + valign: string + disabled: boolean + readonly pressed: boolean + } + + interface Camera extends Base { + rotation: number + clip: boolean + } + + interface Checkbox extends Base { + fontSize: number + checked: boolean + color: string + text: string + } + + interface Circle extends Base { + color: string + fontSize: number + min: number + max: number + lineWidth: number + valueStart: number + value: number + showValue: boolean + } + + interface Input extends Base { + text: string + color: string + type: string + disabled: boolean + } + + interface Joystick extends Base { + color: string + keys: string + text: string + readonly x: number + readonly y: number + } + + interface Led extends Base { + color: string + on: boolean + } + + interface Orientation extends Base { + color: string + readonly yaw: number + readonly pitch: number + readonly roll: number + readonly joystickX: number + readonly joystickY: number + } + + interface Select extends Base { + color: string + background: string + disabled: boolean + options: string + selectedIndex: number + } + + interface Slider extends Base { + color: string + fontSize: number + min: number + max: number + value: number + precision: number + showValue: boolean + } + + interface SpinEdit extends Base { + fontSize: number + color: string + value: number + step: number + precision: number + } + + interface Switcher extends Base { + fontSize: number + color: string + value: number + min: number + max: number + } + + interface Text extends Base { + text: string + fontSize: number + color: string + background: string + align: string + valign: string + prefix: string + suffix: string + } + } + + namespace builder { + interface Base { + css(key: string, value: string): this + finish(): this + } + + interface Arm extends Base { + info(info: Record): Arm + + onGrab(callback: (arm: widget.Arm) => void): Arm + onPositionChanged(callback: (arm: widget.Arm) => void): Arm + } + + interface Bar extends Base { + color(color: string): Bar + fontSize(fontSize: number): Bar + min(min: number): Bar + max(max: number): Bar + value(value: number): Bar + showValue(showValue: boolean): Bar + } + + interface Button extends Base { + text(text: string): Button + fontSize(fontSize: number): Button + color(color: string): Button + background(background: string): Button + align(align: string): Button + valign(valign: string): Button + disabled(disabled: boolean): Button + + onPress(callback: (button: widget.Button) => void): Button + onRelease(callback: (button: widget.Button) => void): Button + } + + interface Camera extends Base { + rotation(rotation: number): Camera + clip(clip: boolean): Camera + tags(tags: any /* TODO: fix type */): Camera + } + + interface Checkbox extends Base { + fontSize(fontSize: number): Checkbox + checked(checked: boolean): Checkbox + color(color: string): Checkbox + text(text: string): Checkbox + + onChanged(callback: (checkbox: widget.Checkbox) => void): Checkbox + } + + interface Circle extends Base { + color(color: string): Circle + fontSize(fontSize: number): Circle + min(min: number): Circle + max(max: number): Circle + lineWidth(lineWidth: number): Circle + valueStart(valueStart: number): Circle + value(value: number): Circle + showValue(showValue: boolean): Circle + } + + interface Input extends Base { + text(text: string): Input + color(color: string): Input + type(type: string): Input + disabled(disabled: boolean): Input + + onChanged(callback: (input: widget.Input) => void): Input + } + + interface Joystick extends Base { + color(color: string): Joystick + keys(keys: string): Joystick + text(text: string): Joystick + + onClick(callback: (joystick: widget.Joystick) => void): Joystick + onPositionChanged(callback: (joystick: widget.Joystick) => void): Joystick + } + + interface Led extends Base { + color(color: string): Led + on(on: boolean): Led + } + + interface Orientation extends Base { + color(color: string): Orientation + + onPositionChanged(callback: (orientation: widget.Orientation) => void): Orientation + } + + interface Select extends Base { + color(color: string): Select + background(background: string): Select + disabled(disabled: boolean): Select + options(options: string): Select + selectedIndex(selectedIndex: number): Select + + onChanged(callback: (select: widget.Select) => void): Select + } + + interface Slider extends Base { + color(color: string): Slider + fontSize(fontSize: number): Slider + min(min: number): Slider + max(max: number): Slider + value(value: number): Slider + precision(precision: number): Slider + showValue(showValue: boolean): Slider + + onChanged(callback: (slider: widget.Slider) => void): Slider + } + + interface SpinEdit extends Base { + fontSize(fontSize: number): SpinEdit + color(color: string): SpinEdit + value(value: number): SpinEdit + step(step: number): SpinEdit + precision(precision: number): SpinEdit + + onChanged(callback: (spinEdit: widget.SpinEdit) => void): SpinEdit + } + + interface Switcher extends Base { + fontSize(fontSize: number): Switcher + color(color: string): Switcher + value(value: number): Switcher + min(min: number): Switcher + max(max: number): Switcher + + onChanged(callback: (switcher: widget.Switcher) => void): Switcher + } + + interface Text extends Base { + text(text: string): Text + fontSize(fontSize: number): Text + color(color: string): Text + background(background: string): Text + align(align: string): Text + valign(valign: string): Text + prefix(prefix: string): Text + suffix(suffix: string): Text + } + + interface Widget extends Base { } + } + + class Builder { + arm(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Arm + bar(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Bar + button(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Button + camera(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Camera + checkbox(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Checkbox + circle(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Circle + input(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Input + joystick(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Joystick + led(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Led + orientation(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Orientation + select(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Select + slider(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Slider + spinEdit(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.SpinEdit + switcher(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Switcher + text(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Text + widget(x: number, y: number, w: number, h: number, uuid?: number, tab?: number): builder.Widget + } + + /** + * Initialize GridUI. + * @param ownerName name of the owner, must match the name entered in RBController app. + * @param deviceName name of this device, visible in the RBController app. + * @param builderCallback callback, which receives the builder instance that can be used to create widgets. + */ + function begin(ownerName: string, deviceName: string, builderCallback: (builder: Builder) => void): void + + /** + * Stop GridUI. + */ + function end(): void + + /** + * Returns included GridUI version as number, to be compared with hex representation of the version. + * + * For example, for version 5.1.0, do: `gridui.version() >= 0x050100` + */ + function version(): number +} \ No newline at end of file diff --git a/docs/robot/lekce1/example-gridui/@types/i2c.d.ts b/docs/robot/lekce1/example-gridui/@types/i2c.d.ts new file mode 100644 index 00000000..fd6685a9 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/i2c.d.ts @@ -0,0 +1,34 @@ +declare module "i2c" { + interface I2C { + /** + * Find an I2C interface by its pin. + * @param pin The pin the I2C interface is connected to. + * @returns The I2C interface, or undefined if not found. + */ + find(pin: number): I2C | undefined; + + /** + * Read from the given address. + * @param address The address to read from. + * @param quantity The number of bytes to read. + * @returns The bytes read. + */ + readFrom(address: number, quantity: number): Uint8Array; + + /** + * Write to the given address. + * @param address The address to write to. + * @param buffer The data to write. + */ + writeTo(address: number, buffer: ArrayBuffer | Uint8Array | number[] | string | number): void; + + /** + * Setup the I2C interface. + * @param options The options to use when setting up the I2C interface. + */ + setup(options: { scl?: number, sda?: number, bitrate?: number }): void; + } + + const I2C1: I2C; + const I2C2: I2C | undefined; +} diff --git a/docs/robot/lekce1/example-gridui/@types/keyvalue.d.ts b/docs/robot/lekce1/example-gridui/@types/keyvalue.d.ts new file mode 100644 index 00000000..761b09fb --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/keyvalue.d.ts @@ -0,0 +1,61 @@ +declare module "keyvalue" { + class KeyValueNamespace { + /** + * Get saved value. + * @param key key, max 15 characters. + * @return saved value or null if not present + */ + get(key: string): string | number | null; + + /** + * Get string saved value. + * @param key key, max 15 characters. + * @return saved value or null if not present or if not string + */ + getString(key: string): string | null; + + /** + * Get number saved value. + * @param key key, max 15 characters. + * @return saved value or null if not present or if not number + */ + getNumber(key: string): number | null; + + /** + * Set value in KeyValue namespace, overwriting any previous value. + * Call commit() after setting everything, otherwise changes will be lost! + * @param key key, max 15 characters. + * @param value value + */ + set(key: string, value: string | number): void; + + /** + * Erase value from KeyValue namespace. + * Call commit() after setting everything, otherwise changes will be lost! + * @param key key, max 15 characters. + */ + erase(key: string): void; + + /** + * Check existance of a key. + * @param key key, max 15 characters. + * @returns true if exists, false otherwise + */ + exists(key: string): boolean; + + /** + * Save modifications to persistent storage. + */ + commit(): void; + } + + /** + * Open a KeyValue namespace for use. KeyValue is a persitent storage for small + * pieces of data that saves values across restarts. + * + * The namespaces are isolated from each other. + * + * @param namespace Namespace name, max 15 characters. + */ + function open(namespace: string): KeyValueNamespace; +} diff --git a/docs/robot/lekce1/example-gridui/@types/ledc.d.ts b/docs/robot/lekce1/example-gridui/@types/ledc.d.ts new file mode 100644 index 00000000..49b016e5 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/ledc.d.ts @@ -0,0 +1,44 @@ +declare module "ledc" { + /** + * Configure the given timer. + * @param timer The timer to configure. + * @param frequency The frequency to configure the timer to. + * @param resolution The resolution to configure the timer to (default 10 bits, changes frequency range) + */ + function configureTimer(timer: number, frequency: number, resolution?: number): void; + + /** + * Configure the given LEDC channel. + * @param channel The channel to configure. + * @param pin The pin to configure the channel to. + * @param timer The timer to configure the channel to. + * @param duty The duty to configure the channel to (0-1023). + */ + function configureChannel(channel: number, pin: number, timer: number, duty: number): void; + + /** + * Set the frequency of the given timer. + * @param timer The timer to set the frequency of. + * @param frequency The frequency to set the timer to. + */ + function setFrequency(timer: number, frequency: number): void; + + /** + * Set the duty of the given channel. + * @param channel The channel to set the duty of. + * @param duty The duty to set the channel to (0-1023). + */ + function setDuty(channel: number, duty: number): void; + + /** + * Stop the given timer. + * @param timer The timer to stop. + */ + function stopTimer(timer: number): void; + + /** + * Stop the given channel. + * @param channel The channel to stop. + */ + function stopChannel(channel: number): void; +} diff --git a/docs/robot/lekce1/example-gridui/@types/misc.d.ts b/docs/robot/lekce1/example-gridui/@types/misc.d.ts new file mode 100644 index 00000000..05e77a20 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/misc.d.ts @@ -0,0 +1,5 @@ +/** + * Exits the current program. + * @param code The exit code to use. + */ +declare function exit(code?: number): void; diff --git a/docs/robot/lekce1/example-gridui/@types/motor.d.ts b/docs/robot/lekce1/example-gridui/@types/motor.d.ts new file mode 100644 index 00000000..2ec31392 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/motor.d.ts @@ -0,0 +1,82 @@ +declare module "motor" { + + type MoveDuration = { + distance?: number; + time?: number; + } + + type MotorPins = { + motA: number; + motB: number; + encA: number; + encB: number; + }; + + type LedcConfig = { + timer: number; + channelA: number; + channelB: number; + }; + + class Motor { + /** + * Construc a new Motor instance + * @param options Motor configuration + * @note Units used in the diameter parameter determines the units used in the other methods + */ + constructor(options: { pins: MotorPins, ledc: LedcConfig, encTicks: number, diameter: number }); + + /** + * Set the speed of the motor + * @param speed Speed in units per second + * @note The units are the same as used in the diameter parameter + */ + setSpeed(speed: number): void; + // setRamp(ramp: number): void; + + /** + * Move the motor + * @param duration Duration of the movement + * @note The units are the same as used in the diameter parameter + * @note If the duration is not provided, the motor will move indefinitely + */ + move(duration?: MoveDuration): Promise; + + /** + * Stop the motor + * @param brake If true, the motor will brake, otherwise it will coast to a stop + */ + stop(brake?: boolean): Promise; + + /** + * Get the position of the motor + * @note The units are the same as used in the diameter parameter + * @returns The position of the motor + */ + getPosition(): number; + + /** + * Close the motor + */ + close(): void; + } + + /* class Wheels { + constructor(options: { + left: MotorPins, + right: MotorPins, + encTicks: number, + diameter: number, + spacing: number + }); + setSpeed(speed: number): void; + // setRamp(ramp: number): void; + + move(curve: number, duration?: MoveDuration): Promise; + // rotate(angle: number): Promise; + stop(brake?: boolean): Promise; + getPosition(): { left: number, right: number }; + + close(): void; + } */ +} diff --git a/docs/robot/lekce1/example-gridui/@types/path.d.ts b/docs/robot/lekce1/example-gridui/@types/path.d.ts new file mode 100644 index 00000000..c4434c60 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/path.d.ts @@ -0,0 +1,36 @@ +declare module "path" { + /** + * Normalize the given path. + * @param path The path to normalize. + * @returns The normalized path. + */ + function normalize(path: string): string; + + /** + * Get the parent directory of the given path. + * @param path The path to get the parent directory of. + * @returns The parent directory. + */ + function dirname(path: string): string; + + /** + * Get the last part of the given path. + * @param path The path to get the last part of. + * @returns The last part of the path. + */ + function basename(path: string): string; + + /** + * Join the given paths. + * @param paths The paths to join. + * @returns The joined path. + */ + function join(...paths: string[]): string; + + /** + * Check if the given path is absolute. + * @param path The path to check. + * @returns True if the path is absolute, false otherwise. + */ + function isAbsolute(path: string): boolean; +} diff --git a/docs/robot/lekce1/example-gridui/@types/platform.d.ts b/docs/robot/lekce1/example-gridui/@types/platform.d.ts new file mode 100644 index 00000000..6fe4c710 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/platform.d.ts @@ -0,0 +1,6 @@ +declare const PlatformInfo: { + /** + * The name of the platform the program is running on. + */ + name: string +} diff --git a/docs/robot/lekce1/example-gridui/@types/pulseCounter.d.ts b/docs/robot/lekce1/example-gridui/@types/pulseCounter.d.ts new file mode 100644 index 00000000..76ad8edd --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/pulseCounter.d.ts @@ -0,0 +1,64 @@ +declare module "pulseCounter" { + type LevelAction = number; + type EdgeAction = number; + + const LevelAction: { + Keep: LevelAction; + Inverse: LevelAction; + Hold: LevelAction; + }; + + const EdgeAction: { + Hold: EdgeAction; + Increase: EdgeAction; + Decrease: EdgeAction; + }; + + type LevelMode = { + low: LevelAction, + high: LevelAction, + } + + type EdgeMode = { + pos: EdgeAction, + neg: EdgeAction, + } + + class PulseCounter { + /** + * Create a new pulse counter instance. + * @param options The options for the pulse counter. + */ + constructor(options: { + pinLevel: number, + pinEdge: number, + levelMode: LevelMode, + edgeMode: EdgeMode, + }) + + /** + * Read the current value of the pulse counter. + */ + read(): number; + + /** + * Reset the pulse counter to 0. + */ + clear(): void; + + /** + * Start the pulse counter. + */ + start(): void; + + /** + * Stop the pulse counter. + */ + stop(): void; + + /** + * Close the pulse counter. + */ + close(): void; + } +} diff --git a/docs/robot/lekce1/example-gridui/@types/simpleradio.d.ts b/docs/robot/lekce1/example-gridui/@types/simpleradio.d.ts new file mode 100644 index 00000000..a1cb8608 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/simpleradio.d.ts @@ -0,0 +1,84 @@ +declare module "simpleradio" { + type PacketDataType = "number" | "string" | "keyvalue"; + + interface PacketInfo { + group: number; + address: string; + rssi: number; + } + + /** + * Initialize the radio. + * @param group The radio group to use. + */ + function begin(group: number): void; + + /** + * Set the radio group. + * @param group The radio group to use, between 0 and 15 inclusive. + */ + function setGroup(group: number): void; + + /** + * Get current radio group + * @returns ID of the current group + */ + function group(): number; + + /** + * Get the local device address. + * @returns the local device address. Only works after begin() is called. + */ + function address(): string; + + /** + * Send a string. + * @param str The string to send. + */ + function sendString(str: string): void; + + /** + * Send a number. + * @param num The number to send. + */ + function sendNumber(num: number): void; + + /** + * Send a key-value pair. + * @param key The key to send. + * @param value The number to send. + */ + function sendKeyValue(key: string, value: number): void; + + /** + * Register a callback for a packet type. + * @param type The packet type to register for. + * @param callback The callback to register. + */ + function on(type: "number", callback: (num: number, info: PacketInfo) => void): void; + + /** + * Register a callback for a packet type. + * @param type The packet type to register for. + * @param callback The callback to register. + */ + function on(type: "string", callback: (str: string, info: PacketInfo) => void): void; + + /** + * Register a callback for a packet type. + * @param type The packet type to register for. + * @param callback The callback to register. + */ + function on(type: "keyvalue", callback: (key: string, value: number, info: PacketInfo) => void): void; + + /** + * Unregister a callback for a packet type. + * @param type The packet type to unregister for. + */ + function off(type: PacketDataType): void; + + /** + * Stop the radio. + */ + function end(): void; +} diff --git a/docs/robot/lekce1/example-gridui/@types/smartled.d.ts b/docs/robot/lekce1/example-gridui/@types/smartled.d.ts new file mode 100644 index 00000000..a9c51e80 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/smartled.d.ts @@ -0,0 +1,55 @@ +declare module "smartled" { + interface Rgb { + r: number; + g: number; + b: number; + } + + interface LedType { + T0H: number; + T1H: number; + T0L: number; + T1L: number; + TRS: number; + } + + class SmartLed { + /** + * Create a new Smart LED strip. + * @param pin The pin the strip is connected to. + * @param count The number of LEDs in the strip. + * @param type The type of LED strip. + */ + constructor(pin: number, count: number, type?: LedType); + + /** + * Show the current buffer on the strip. + */ + public show(): void; + + /** + * Set the color of the given LED. + * @param index The index of the LED to set. + * @param rgb The color to set the LED to. + */ + public set(index: number, rgb: Rgb): void; + + /** + * Get the color of the given LED. + * @param index The index of the LED to get. + * @returns The color of the LED. + */ + public get(index: number): Rgb; + + /** + * Clear the buffer. + */ + public clear(): void; + } + + const LED_WS2812: LedType; + const LED_WS2812B: LedType; + const LED_WS2812B_2020: LedType; + const LED_SK6812: LedType; + const LED_WS2813: LedType; +} diff --git a/docs/robot/lekce1/example-gridui/@types/stdio.d.ts b/docs/robot/lekce1/example-gridui/@types/stdio.d.ts new file mode 100644 index 00000000..f0c1881d --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/stdio.d.ts @@ -0,0 +1,13 @@ +declare module "stdio" { + let stdout: Writable; + let stderr: Writable; + let stdin: Readable; +} + +declare const console: { + debug(arg: any): void; + log(arg: any): void; + warn(arg: any): void; + info(arg: any): void; + error(arg: any): void; +} diff --git a/docs/robot/lekce1/example-gridui/@types/timers.d.ts b/docs/robot/lekce1/example-gridui/@types/timers.d.ts new file mode 100644 index 00000000..4f73380a --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/timers.d.ts @@ -0,0 +1,31 @@ +/** + * Returns a promise that resolves after the specified time. + * @param ms The number of milliseconds to wait before resolving the promise. + */ +declare function sleep(ms: number): Promise; + +/** + * Calls a function after the specified time. + * @param callback The function to call. + * @param ms The number of milliseconds to wait before calling the function. + */ +declare function setTimeout(callback: () => void, ms: number): number; + +/** + * Calls a function repeatedly, with a fixed time delay between each call. + * @param callback The function to call. + * @param ms The number of milliseconds to wait before calling the function. + */ +declare function setInterval(callback: () => void, ms: number): number; + +/** + * Cancels a timeout previously established by calling setTimeout(). + * @param id The identifier of the timeout to cancel. + */ +declare function clearTimeout(id: number): void; + +/** + * Cancels a timeout previously established by calling setInterval(). + * @param id The identifier of the interval to cancel. + */ +declare function clearInterval(id: number): void; diff --git a/docs/robot/lekce1/example-gridui/@types/timestamp.d.ts b/docs/robot/lekce1/example-gridui/@types/timestamp.d.ts new file mode 100644 index 00000000..bd9ab6c8 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/timestamp.d.ts @@ -0,0 +1,11 @@ +declare interface Timestamp { + /** + * Convert the timestamp to milliseconds. + */ + millis(): number; + + /** + * Get the lower 10^3 part of the timestamp in microseconds. + */ + micros(): number; +} diff --git a/docs/robot/lekce1/example-gridui/@types/wifi.d.ts b/docs/robot/lekce1/example-gridui/@types/wifi.d.ts new file mode 100644 index 00000000..7e532194 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/@types/wifi.d.ts @@ -0,0 +1,6 @@ +declare module "wifi" { + /** + * Return current IPv4 of the device, or null if WiFi is disabled or not connected. + */ + function currentIp(): string | null; +} diff --git a/docs/robot/lekce1/example-gridui/src/index.ts b/docs/robot/lekce1/example-gridui/src/index.ts new file mode 100644 index 00000000..e1eddd1d --- /dev/null +++ b/docs/robot/lekce1/example-gridui/src/index.ts @@ -0,0 +1,76 @@ +import * as Robutek from "./libs/robutek.js" +import * as wifi from "wifi"; +import Layout from "./layout.js" + +Robutek.init() // Zprovozní Robůtka + +function scale(value) { + // Scale the joystick value from -32768..32767 to -1..1 + return value / 32768; +} + +function limitSpeed(value, factor) { + // Apply a limiting factor to the value + return value * factor; +} + +function setMotorsJoystick(x: number, y: number, coef = 2.5) { + // Scale the joystick values + x = scale(x); + y = scale(y); + + // Calculate motor powers + let r = (y - (x / coef)); + let l = (y + (x / coef)); + + // Apply speed limiter + r = limitSpeed(r, speedLimiter); + l = limitSpeed(l, speedLimiter); + + // Ensure the values are within the range -1 to 1 + r = Math.max(-1, Math.min(1, r)); + l = Math.max(-1, Math.min(1, l)); + + // Swap r and l if both are negative + if (r < 0 && l < 0) { + [r, l] = [l, r]; + } + + console.log(`x: ${x}, y: ${y}`); + console.log(`left motor power: ${l}, right motor power: ${r}`); + + // Set motor power + Robutek.LeftMot.setSpeed(l); + Robutek.LeftMot.move(); + Robutek.RightMot.setSpeed(r); + Robutek.RightMot.move(); +} + + +let speedLimiter = 0.5; + +Layout.begin("Owner name", "Device Name", builder => { + + builder.ButtonBlink.onPress(async () => { + Robutek.ledStrip.clear(); + Robutek.ledStrip.set(0, { r: 255, g: 0, b: 0 }); + Robutek.ledStrip.show(); + await sleep(500); + Robutek.ledStrip.clear(); + Robutek.ledStrip.set(0, { r: 0, g: 255, b: 0 }); + Robutek.ledStrip.show(); + await sleep(500); + }); + + builder.SetSpeed.onChanged(slider => { + speedLimiter = slider.value; + console.log(`speed limiter: ${speedLimiter}`); + }); + + builder.Joystick.onPositionChanged(joystick => { + setMotorsJoystick(joystick.x, joystick.y); + }); + +}) + +console.log("Otevři aplikaci RBController nebo otevři prohlížeč a zadej IP: " + wifi.currentIp() + " pro ovládání robota."); \ No newline at end of file diff --git a/docs/robot/lekce1/example-gridui/src/layout.ts b/docs/robot/lekce1/example-gridui/src/layout.ts new file mode 100644 index 00000000..b87c4cd4 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/src/layout.ts @@ -0,0 +1,88 @@ +// AUTOGENERATED FILE, DO NOT EDIT +// Generated by https://gridui.robotikabrno.cz/ +// Layout: {"cols":12,"rows":18,"enableSplitting":true,"widgets":[{"uuid":43594,"type":"Button","state":{"id":"ButtonBlink","x":1,"y":1.5,"w":10,"h":2,"tab":0,"css":{},"text":"Blikni","fontSize":18,"color":"#fb0404","background":"","align":"center","valign":"center","disabled":false}},{"uuid":11866,"type":"Joystick","state":{"id":"Joystick","x":3,"y":10,"w":6,"h":5.5,"tab":0,"css":{},"color":"#FF0000","keys":"","text":""}},{"uuid":4862,"type":"Slider","state":{"id":"SetSpeed","x":1,"y":4,"w":10,"h":2.5,"tab":0,"css":{},"color":"#008000","fontSize":16,"min":0,"max":1,"value":0.5,"precision":0.05,"showValue":true}},{"uuid":47586,"type":"Text","state":{"id":"TextSpeed","x":1,"y":6.5,"w":10,"h":1,"tab":0,"css":{},"text":"Nastavení maximální rychlosti","fontSize":17,"color":"#000000","background":"","align":"center","valign":"center","prefix":"","suffix":""}},{"uuid":26977,"type":"Text","state":{"id":"TextInfo","x":1,"y":0,"w":10,"h":1,"tab":0,"css":{},"text":"Řízení Robůtka","fontSize":19,"color":"#000000","background":"","align":"center","valign":"center","prefix":"","suffix":""}}]} + +// Add this as a file layout.ts to your project. +// +// Inicialization: +// +// import Layout from "./layout.js" +// +// Layout.begin("Owner name", "Device Name", builder => { +// +// // Add calback handlers here, like this: +// builder.Button1.onPress(btn => { +// console.log("press") +// }) +// +// }) +// +// Usage later in code: +// +// Layout.Button1.color = "red"; +// console.log(Layout.Button1.pressed) +// + +import * as gridui from "gridui" + +if (gridui.version() < 0x040000) { + throw new Error("Your RBGridUi library version is too low for this layout, please update to 040000.") +} + +interface LayoutBuilder { + readonly ButtonBlink: gridui.builder.Button + readonly Joystick: gridui.builder.Joystick + readonly SetSpeed: gridui.builder.Slider + readonly TextSpeed: gridui.builder.Text + readonly TextInfo: gridui.builder.Text +} + +interface Layout { + readonly ButtonBlink: gridui.widget.Button + readonly Joystick: gridui.widget.Joystick + readonly SetSpeed: gridui.widget.Slider + readonly TextSpeed: gridui.widget.Text + readonly TextInfo: gridui.widget.Text + + begin(ownerName: string, deviceName: string, builderCallback?: (layoutBuilder: LayoutBuilder) => void): void + + changeTab(index: number): void + log(message: string): void +} + +const layout = { + begin(ownerName: string, deviceName: string, builderCallback?: (layoutBuilder: LayoutBuilder) => void) { + gridui.begin(ownerName, deviceName, (builder) => { + const layoutBuilder: LayoutBuilder = { + ButtonBlink: builder.button(1, 1.5, 10, 2, 43594) + .text("Blikni") + .fontSize(18) + .color("#fb0404"), + Joystick: builder.joystick(3, 10, 6, 5.5, 11866), + SetSpeed: builder.slider(1, 4, 10, 2.5, 4862) + .max(1) + .value(0.5) + .precision(0.05), + TextSpeed: builder.text(1, 6.5, 10, 1, 47586) + .text("Nastavení maximální rychlosti") + .fontSize(17), + TextInfo: builder.text(1, 0, 10, 1, 26977) + .text("Řízení Robůtka") + .fontSize(19) + } + + if (builderCallback !== undefined) { + builderCallback(layoutBuilder) + } + + for (const key in layoutBuilder) { + layout[key] = layoutBuilder[key].finish() + layoutBuilder[key] = undefined + } + }) + }, + changeTab: gridui.changeTab, + log: gridui.log, +} as Layout + +export default layout \ No newline at end of file diff --git a/docs/robot/lekce1/example-gridui/src/libs/colors.ts b/docs/robot/lekce1/example-gridui/src/libs/colors.ts new file mode 100644 index 00000000..de76a121 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/src/libs/colors.ts @@ -0,0 +1,81 @@ +/** + * Barva je jednoduchá trojice červené, zelené, a modré složky + * - R: červená (rozsah 0-255) + * - G: zelená (rozsah 0-255) + * - B: modrá (rozsah 0-255) + */ +interface Rgb { + r: number; + g: number; + b: number; +} + + +/** + * Alternativní způsob, jak vyjádřit barvu, je HSL: + * - Hue: odstín (rozsah 0-360) + * - Saturation: sytost barev (rozsah 0-1) + * - Lightness: světlost (rozsah 0-1) + */ +interface Hsl { + h: number; + s: number; + l: number; +} + +/** + * Mezi jednotlivými reprezentacemi lze převádět + * @param hsl {Number} Hue (0-360), Saturation (0-1), Lightness (0-1) + * @returns {Rgb} Red (0-255), Green (0-255), Blue (0-255) + */ +export function hsl_to_rbg( hsl: Hsl ) : Rgb { + const chroma = ( 1 - Math.abs( 2 * hsl.l - 1 ) * hsl.s ); + const hue = hsl.h / 60; + const x = chroma * ( 1 - Math.abs( ( hue % 2 ) - 1 ) ); + + let color : Rgb = { r: 0, g: 0, b: 0 }; + if( hue > 0 && hue < 1 ){ + color = { r: chroma, g: x, b: 0 }; + } else if( hue >= 1 && hue < 2 ){ + color = { r: x, g: chroma, b: 0 }; + } else if( hue >= 2 && hue < 3 ){ + color = { r: 0, g: chroma, b: x }; + } else if( hue >= 3 && hue < 4 ){ + color = { r: 0, g: x, b: chroma }; + } else if( hue >= 4 && hue < 5 ){ + color = { r: x, g: 0, b: chroma }; + } else { + color = { r: chroma, g: 0, b: x }; + } + const correction = hsl.l - chroma / 2; + color.r = ( color.r + correction ) * 255; + color.g = ( color.g + correction ) * 255; + color.b = ( color.b + correction ) * 255; + + return color; +} + +/** + * Funkce rainbow zafixuje sytost a světlost, a prochází barvami + * @param hue (0-360) + * @param brightness (0-100) - 50 je defaultní hodnota + * @returns {Rgb} + */ +export function rainbow( hue: number, brightness: number = 50) : Rgb { + hue = Math.min( hue, 360 ); // Zajistíme, že zadaná hodnota není mimo rozsah + // fix range to 0-100 + let brightness_mapped = Math.min(Math.max(brightness, 0), 100); + return hsl_to_rbg( { h: hue, s: 1, l: brightness_mapped / 100 } ); +} + +/* Základní barvy pro LED pásky*/ +export const red = rainbow( 0 ); +export const orange = rainbow( 27 ); +export const yellow = rainbow( 54 ); +export const green = rainbow( 110 ); +export const light_blue = rainbow( 177 ); +export const blue = rainbow( 240 ); +export const purple = rainbow( 285 ); +export const pink = rainbow( 323 ); +export const white : Rgb = { r: 100, g: 100, b: 100 }; +export const off : Rgb = { r: 0, g: 0, b: 0 }; \ No newline at end of file diff --git a/docs/robot/lekce1/example-gridui/src/libs/readline.ts b/docs/robot/lekce1/example-gridui/src/libs/readline.ts new file mode 100644 index 00000000..4372efd2 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/src/libs/readline.ts @@ -0,0 +1,84 @@ +import { stdout, stdin } from "stdio"; + +/** + * A class for reading standard input line by line. + */ + +export class readline { + private buffer: string = ""; + private promise: Promise | null = null; + private resolve: ((value: string) => void) | null = null; + private reject: ((reason: any) => void) | null = null; + private closed: boolean = false; + private echo: boolean; + + private onGet(str: string) { + if (this.echo) { + stdout.write(str); + } + + if (str == "\n") { + if (this.resolve) { + this.resolve(this.buffer); + } + + this.buffer = ""; + this.promise = null; + this.resolve = null; + this.reject = null; + + return; + } + + this.buffer += str; + + if (!this.closed) { + stdin.get() + .then((data) => this.onGet(data)) + .catch((reason) => { + if (this.reject) { + this.reject(reason); + } + }); + } + + } + + constructor(echo: boolean = false) { + this.echo = echo; + } + + public read(): Promise { + if (this.promise != null) { + return Promise.reject("Already reading"); + } + + this.promise = new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + }); + + stdin.get() + .then((data) => this.onGet(data)) + .catch((reason) => { + if (this.reject) { + this.reject(reason); + } + }); + + return this.promise; + } + + public close() { + this.closed = true; + + if (this.reject) { + this.reject("Stopped"); + } + + this.buffer = ""; + this.promise = null; + this.resolve = null; + this.reject = null; + } +} diff --git a/docs/robot/lekce1/example-gridui/src/libs/robutek.ts b/docs/robot/lekce1/example-gridui/src/libs/robutek.ts new file mode 100644 index 00000000..a6a934b1 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/src/libs/robutek.ts @@ -0,0 +1,139 @@ + +import * as adc from "adc"; +import * as gpio from "gpio"; + +import { Servo } from "./servo.js"; + +import { SmartLed, LED_WS2812 } from "smartled"; + +import * as motors from "motor" + +const leftMotorPins: motors.MotorPins = { motA: 11, motB: 12, encA: 39, encB: 40 } +const rightMotorPins: motors.MotorPins = { motA: 45, motB: 13, encA: 41, encB: 42 } + +const leftMotorLedc: motors.LedcConfig = { timer: 1, channelA: 0, channelB: 1 } +const rightMotorLedc: motors.LedcConfig = { timer: 1, channelA: 2, channelB: 3 } + +export const LeftMot = new motors.Motor({ pins: leftMotorPins, ledc: leftMotorLedc, encTicks: 400, diameter: 10 }); +export const RightMot = new motors.Motor({ pins: rightMotorPins, ledc: rightMotorLedc, encTicks: 400, diameter: 10 }); + + +export function init() { + adc.configure(LineSensors.S_1, adc.Attenuation.Db0); + adc.configure(LineSensors.S_2, adc.Attenuation.Db0); + adc.configure(LineSensors.S_3, adc.Attenuation.Db0); + adc.configure(LineSensors.S_4, adc.Attenuation.Db0); + + gpio.pinMode(LineSensors.S_PWR, gpio.PinMode.OUTPUT); + gpio.pinMode(LineSensors.S_SW, gpio.PinMode.OUTPUT); + + gpio.write(LineSensors.S_PWR, 1); +} + + + +type MoveDuration = { + distance?: number; // distance in cm + speed?: number; // speed in mm/s? +} + + +export type SensorType = 'W_FR' | 'W_FL' | 'W_BL' | 'W_BR' | 'L_FR' | 'L_FL' | 'L_BL' | 'L_BR'; +export class LineSensors { + + public static readonly S_1: number = 4; + public static readonly S_2: number = 5; + public static readonly S_3: number = 6; + public static readonly S_4: number = 7; + public static readonly S_SW: number = 8; + public static readonly S_PWR: number = 47; + + private sw: number = 0; + + + private async switch_sensors(to_value: number) { + if (to_value == this.sw) { + return; + } + this.sw = to_value; + gpio.write(LineSensors.S_SW, to_value); + await sleep(5); + } + + public async read(sensor: SensorType): Promise { + switch (sensor) { + case 'W_FR': + this.switch_sensors(0); + return adc.read(LineSensors.S_1); + + case 'W_FL': + this.switch_sensors(0); + return adc.read(LineSensors.S_2); + + case 'W_BL': + this.switch_sensors(0); + return adc.read(LineSensors.S_3); + + case 'W_BR': + this.switch_sensors(0); + return adc.read(LineSensors.S_4); + + + case 'L_FR': + this.switch_sensors(1); + return adc.read(LineSensors.S_1); + + case 'L_FL': + this.switch_sensors(1); + return adc.read(LineSensors.S_2); + + case 'L_BL': + this.switch_sensors(1); + return adc.read(LineSensors.S_3); + + case 'L_BR': + this.switch_sensors(1); + return adc.read(LineSensors.S_4); + + default: + return 0; + + } + } +} + +export class Pen { + private servo: Servo; + + public static readonly UP = 512 + 180; + public static readonly DOWN = 512 - 180; + public static readonly MIDDLE = 512; + public static readonly UNLOAD = 0; + + constructor(pin: number) { + this.servo = new Servo(pin, 1, 0); + } + + /** + * Set the pen servo position. + * @param value The position to set the servo to, from 0 to 1023. + */ + public move(value: number) { + this.servo.write(value); + } +} + +export const ledStrip = new SmartLed(48, 9, LED_WS2812); + +export class Pins { + public static readonly Status_LED: number = 46; + + public static readonly Motor1_A = 11; + public static readonly Motor1_B = 12; + public static readonly Motor2_A = 45; + public static readonly Motor2_B = 13; + public static readonly ENC1_1 = 39; + public static readonly ENC1_2 = 40; + public static readonly ENC2_1 = 41; + public static readonly ENC2_2 = 42; +} \ No newline at end of file diff --git a/docs/robot/lekce1/example-gridui/src/libs/servo.ts b/docs/robot/lekce1/example-gridui/src/libs/servo.ts new file mode 100644 index 00000000..ac4fe3ee --- /dev/null +++ b/docs/robot/lekce1/example-gridui/src/libs/servo.ts @@ -0,0 +1,34 @@ +import * as ledc from "ledc"; + +/** + * A class for controlling a servo motor. + */ +export class Servo { + private channel: number; // the PWM channel to use + + /** + * Create a new servo object. + * @param pin The pin the servo is connected to. + * @param timer The timer to use for PWM. + * @param channel The channel to use for PWM. + */ + constructor(pin: number, timer: number, channel: number) { + this.channel = channel; + ledc.configureTimer(timer, 50, 12); // 50Hz is the frequency of a servo, 12 is the resolution of the timer + ledc.configureChannel(channel, pin, timer, 1023); // 1023 is the resolution of a servo + } + + /** + * Set the servo position. + * @param value The position to set the servo to, from 0-1023. + */ + write(value: number) { + // map the value from 0-1023 to 0.5-2.5ms + const ms = ((value / 1023) * 2) + 0.5; // 0.5-2.5ms is the range of a servo + + // convert to a duty cycle + const duty = (ms / 20) * 1023; // 20ms is the period of a servo + + ledc.setDuty(this.channel, duty); // set the duty cycle to the servo + } +} diff --git a/docs/robot/lekce1/example-gridui/src/libs/simplemotors.ts b/docs/robot/lekce1/example-gridui/src/libs/simplemotors.ts new file mode 100644 index 00000000..c6f424cd --- /dev/null +++ b/docs/robot/lekce1/example-gridui/src/libs/simplemotors.ts @@ -0,0 +1,47 @@ +import * as ledc from "ledc"; + +/** + * A class for controlling a servo motor. + */ +export class Motor { + private channel1: number; // the PWM channel to use + private channel2: number; // the PWM channel to use + + /** + * Create a new servo object. + * @param pin1 The pin the servo is connected to. + * @param pin2 The pin the servo is connected to. + * @param timer The timer to use for PWM. + * @param channel1 The channel to use for PWM. + * @param channel2 The channel to use for PWM. + */ + + constructor(pin1: number, pin2: number, timer: number, channel1: number, channel2: number) { + this.channel1 = channel1; + this.channel2 = channel2; + ledc.configureTimer(timer, 50, 12); // 50Hz is the frequency of a servo, 12 is the resolution of the timer + ledc.configureChannel(channel1, pin1, timer, 1023); // 1023 is the resolution of a servo + ledc.configureChannel(channel2, pin2, timer, 1023); // 1023 is the resolution of a servo + } + + /** + * Set the servo position. + * @param value The position to set the servo to, from -1 to 1. + */ + write(value: number) { + console.log(value) + + if (value > 0) { + ledc.setDuty(this.channel1, 1); + ledc.setDuty(this.channel2, (Math.abs(value) * 1023)); + } + else if (value < 0) { + ledc.setDuty(this.channel1, (Math.abs(value) * 1023)); + ledc.setDuty(this.channel2, 1); + } + else { + ledc.setDuty(this.channel1, 1); + ledc.setDuty(this.channel2, 1); + } + } +} diff --git a/docs/robot/lekce1/example-gridui/tsconfig.json b/docs/robot/lekce1/example-gridui/tsconfig.json new file mode 100644 index 00000000..8cf82b10 --- /dev/null +++ b/docs/robot/lekce1/example-gridui/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es2020", + "module": "es2020", + "lib": ["es2020"], + "moduleResolution": "node", + "sourceMap": false, + "outDir": "build", + "rootDir": "src", + } +}