From afb3bb4a0778effe52af59529575ac83d34f57d8 Mon Sep 17 00:00:00 2001 From: Rohit Shetty <138729620+28shettr@users.noreply.github.com> Date: Wed, 13 May 2026 19:41:39 -0500 Subject: [PATCH 01/12] Add NextCRServo wrapper for continuous-rotation servos --- .../nextftc/hardware/servos/NextCRServo.kt | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt new file mode 100644 index 0000000..eb03c85 --- /dev/null +++ b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2026 NextFTC Team + * + * Use of this source code is governed by an BSD-3-clause + * license that can be found in the LICENSE.md file at the root of this repository or at + * https://opensource.org/license/bsd-3-clause. + */ + +package dev.nextftc.hardware.servos + +import com.qualcomm.robotcore.hardware.CRServoImplEx +import com.qualcomm.robotcore.hardware.DcMotorSimple +import dev.nextftc.hardware.Caching +import dev.nextftc.hardware.LazyHardware +import dev.nextftc.hardware.RobotController + +/** + * Lightweight wrapper around a [CRServoImplEx] that provides a more user-friendly + * interface for controlling continuous-rotation servo power and direction. + * + * Example: + * val crServo = NextCRServo("intakeServo") + * crServo.power = 0.75 + * crServo.direction = DcMotorSimple.Direction.REVERSE + * + * @param initializer A function returning the backing [CRServoImplEx]. It will be + * invoked lazily the first time the servo is accessed. + * @param cacheTolerance Tolerance used by the [Caching] delegate for + * power updates; defaults to 0.01. + */ +class NextCRServo(initializer: () -> CRServoImplEx, val cacheTolerance: Double = 0.01) { + @JvmOverloads constructor(name: String, cacheTolerance: Double = 0.01) : this( + { RobotController.hardwareMap[name] as CRServoImplEx }, + cacheTolerance, + ) + + private val servo by LazyHardware(initializer) + + /** + * Power applied to the servo, in the range [-1.0, 1.0]. + */ + val power: Double by Caching(cacheTolerance) { + if (it != null) { + servo.power = it + } + } + + /** + * Direction of the servo. Setting this to [DcMotorSimple.Direction.REVERSE] + * causes positive [power] values to spin the servo the opposite way, + * and vice versa. + */ + var direction: DcMotorSimple.Direction + get() = servo.direction + set(value) { + servo.direction = value + } + + /** + * Sets the servo's direction to [DcMotorSimple.Direction.REVERSE], causing + * positive power values to spin the servo the opposite way. + */ + fun reverse() = apply { + direction = DcMotorSimple.Direction.REVERSE + } + + /** + * Enables the PWM output of the associated servo. + */ + fun enable() { + servo.setPwmEnable() + } + + /** + * Disables the PWM output of the associated servo. + */ + fun disable() { + servo.setPwmDisable() + } +} \ No newline at end of file From 6b2dbe6ab4aaccf1aa9cf9e0f7360fed0ab71fdf Mon Sep 17 00:00:00 2001 From: Rohit Shetty <138729620+28shettr@users.noreply.github.com> Date: Wed, 13 May 2026 21:03:24 -0500 Subject: [PATCH 02/12] Next CR Servo now is an open file --- .../src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt index eb03c85..daad905 100644 --- a/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt +++ b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt @@ -28,7 +28,7 @@ import dev.nextftc.hardware.RobotController * @param cacheTolerance Tolerance used by the [Caching] delegate for * power updates; defaults to 0.01. */ -class NextCRServo(initializer: () -> CRServoImplEx, val cacheTolerance: Double = 0.01) { +open class NextCRServo(initializer: () -> CRServoImplEx, val cacheTolerance: Double = 0.01) { @JvmOverloads constructor(name: String, cacheTolerance: Double = 0.01) : this( { RobotController.hardwareMap[name] as CRServoImplEx }, cacheTolerance, From 1d5ec6c8df2a8851348b9e6f0b7b12fa4213f290 Mon Sep 17 00:00:00 2001 From: Rohit Shetty <138729620+28shettr@users.noreply.github.com> Date: Wed, 13 May 2026 21:05:08 -0500 Subject: [PATCH 03/12] Add NextFeedbackServo and NextFeedbackCRServo for analog feedback --- .../hardware/servos/NextFeedbackCRServo.kt | 44 +++++++++++++++++ .../hardware/servos/NextFeedbackServo.kt | 48 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextFeedbackCRServo.kt create mode 100644 hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextFeedbackServo.kt diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextFeedbackCRServo.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextFeedbackCRServo.kt new file mode 100644 index 0000000..03714b7 --- /dev/null +++ b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextFeedbackCRServo.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026 NextFTC Team + * + * Use of this source code is governed by an BSD-3-clause + * license that can be found in the LICENSE.md file at the root of this repository or at + * https://opensource.org/license/bsd-3-clause. + */ + +package dev.nextftc.hardware.servos + +import com.qualcomm.robotcore.hardware.AnalogInput +import dev.nextftc.hardware.AnalogFeedback +import dev.nextftc.hardware.LazyHardware +import dev.nextftc.hardware.RobotController + +/** + * A [NextCRServo] paired with an analog feedback input for reading the servo's + * actual angle. Useful for continuous-rotation servos with feedback wires + * (e.g. Axon CR) where you want to know how far the servo has rotated. + * + * Inherits everything from [NextCRServo] — `power`, `direction`, `reverse()`, + * `enable()`, `disable()` — and adds [angleInRadians] or [angleInDegrees] for reading the physical angle in + * radians or degrees respectively from the feedback input. + * + * @param servoName Hardware map name of the servo. + * @param feedbackName Hardware map name of the analog input. + * @param cacheTolerance Tolerance for the [NextCRServo] power caching delegate. + */ +class NextFeedbackCRServo( + servoName: String, + feedbackName: String, + cacheTolerance: Double = 0.01, + ) : NextCRServo(servoName, cacheTolerance) { + + private val analogInput by LazyHardware { + RobotController.hardwareMap[feedbackName] as AnalogInput + } + + /** Actual angle of the servo, in RADIANS. */ + val angleInRadians: Double by AnalogFeedback { analogInput.voltage } + + /** Actual angle of the servo, in DEGREES. */ + val angleInDegrees: Double get() = Math.toDegrees(angleInRadians) +} \ No newline at end of file diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextFeedbackServo.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextFeedbackServo.kt new file mode 100644 index 0000000..8329708 --- /dev/null +++ b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextFeedbackServo.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026 NextFTC Team + * + * Use of this source code is governed by an BSD-3-clause + * license that can be found in the LICENSE.md file at the root of this repository or at + * https://opensource.org/license/bsd-3-clause. + */ + +package dev.nextftc.hardware.servos +import com.qualcomm.robotcore.hardware.AnalogInput +import dev.nextftc.hardware.AnalogFeedback +import dev.nextftc.hardware.LazyHardware +import dev.nextftc.hardware.RobotController + +/** + * A [NextServo] paired with an analog feedback input for reading the servo's + * actual angle. Useful for servos with a feedback wire (e.g. Axon). + * + * Inherits everything from [NextServo] — `position`, `pwmRange`, `enable()`, + * `disable()` — and adds [angleInRadians] or [angleInDegrees] for reading the physical angle in + * radians or degrees respectively from the feedback input. + * + * Example: + * val arm = NextFeedbackServo("armServo", "armEncoder") + * arm.position = 0.5 + * val angle = arm.angleInRadians // where it actually is, in RADIANS + * + * @param servoName Hardware map name of the servo. + * @param feedbackName Hardware map name of the analog input. + * @param cacheTolerance Tolerance for the [NextServo] position caching delegate. + */ +class NextFeedbackServo( + servoName: String, + feedbackName: String, + cacheTolerance: Double = 0.01, + ) : NextServo(servoName, cacheTolerance) { + + private val analogInput by LazyHardware { + RobotController.hardwareMap[feedbackName] as AnalogInput + } + + /** Actual angle of the servo, in RADIANS. */ + val angleInRadians: Double by AnalogFeedback { analogInput.voltage } + + /** Actual angle of the servo, in DEGREES. */ + val angleInDegrees: Double get() = Math.toDegrees(angleInRadians) + +} \ No newline at end of file From 99bf47bc38946ae301864724bf639a7f4cfffe44 Mon Sep 17 00:00:00 2001 From: Rohit Shetty <138729620+28shettr@users.noreply.github.com> Date: Tue, 19 May 2026 18:47:03 -0500 Subject: [PATCH 04/12] commit --- .../src/main/kotlin/dev/nextftc/hardware/servos/NextServo.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextServo.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextServo.kt index 5622ce9..44c0dce 100644 --- a/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextServo.kt +++ b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextServo.kt @@ -20,6 +20,7 @@ import dev.nextftc.hardware.RobotController * controls how sensitive the [position] caching delegate is to small changes. * * Example: + * * val servo = NextServo("armServo") * servo.position = 0.5 * servo.setPwmRange(500.0, 2500.0) @@ -29,7 +30,7 @@ import dev.nextftc.hardware.RobotController * @param cacheTolerance Tolerance used by the [Caching] delegate for * position updates; defaults to 0.01. */ -class NextServo(initializer: () -> ServoImplEx, val cacheTolerance: Double = 0.01) { +open class NextServo(initializer: () -> ServoImplEx, val cacheTolerance: Double = 0.01) { @JvmOverloads constructor(name: String, cacheTolerance: Double = 0.01) : this( { RobotController.hardwareMap[name] as ServoImplEx }, cacheTolerance, From 208bfc96db75062581892d4920a63d8f75d96adf Mon Sep 17 00:00:00 2001 From: Rohit Shetty <138729620+28shettr@users.noreply.github.com> Date: Tue, 19 May 2026 18:47:18 -0500 Subject: [PATCH 05/12] cleaned up docs --- .../src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt | 3 +++ .../kotlin/dev/nextftc/hardware/servos/NextFeedbackServo.kt | 2 ++ 2 files changed, 5 insertions(+) diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt index daad905..9cf7b00 100644 --- a/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt +++ b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextCRServo.kt @@ -19,9 +19,12 @@ import dev.nextftc.hardware.RobotController * interface for controlling continuous-rotation servo power and direction. * * Example: + * + *``` * val crServo = NextCRServo("intakeServo") * crServo.power = 0.75 * crServo.direction = DcMotorSimple.Direction.REVERSE + *``` * * @param initializer A function returning the backing [CRServoImplEx]. It will be * invoked lazily the first time the servo is accessed. diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextFeedbackServo.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextFeedbackServo.kt index 8329708..5122aac 100644 --- a/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextFeedbackServo.kt +++ b/hardware/src/main/kotlin/dev/nextftc/hardware/servos/NextFeedbackServo.kt @@ -21,9 +21,11 @@ import dev.nextftc.hardware.RobotController * radians or degrees respectively from the feedback input. * * Example: + * ``` * val arm = NextFeedbackServo("armServo", "armEncoder") * arm.position = 0.5 * val angle = arm.angleInRadians // where it actually is, in RADIANS + *``` * * @param servoName Hardware map name of the servo. * @param feedbackName Hardware map name of the analog input. From 55b4e6fb9170b5b4c7861e1a2a5603d8f09d17a7 Mon Sep 17 00:00:00 2001 From: Rohit Shetty <138729620+28shettr@users.noreply.github.com> Date: Tue, 19 May 2026 18:59:22 -0500 Subject: [PATCH 06/12] commit --- .idea/caches/deviceStreaming.xml | 1773 ++++++++++++++++++++++++++++++ .idea/codeStyles/Project.xml | 157 +++ .idea/markdown.xml | 8 + .idea/migrations.xml | 10 + .idea/runConfigurations.xml | 17 + 5 files changed, 1965 insertions(+) create mode 100644 .idea/caches/deviceStreaming.xml create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/markdown.xml create mode 100644 .idea/migrations.xml create mode 100644 .idea/runConfigurations.xml diff --git a/.idea/caches/deviceStreaming.xml b/.idea/caches/deviceStreaming.xml new file mode 100644 index 0000000..2d334f0 --- /dev/null +++ b/.idea/caches/deviceStreaming.xml @@ -0,0 +1,1773 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..9c3d95a --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/markdown.xml b/.idea/markdown.xml new file mode 100644 index 0000000..c61ea33 --- /dev/null +++ b/.idea/markdown.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file From 383501c7a2e59ea7dcf5c9703ae318511c27cc7c Mon Sep 17 00:00:00 2001 From: Rohit Shetty <138729620+28shettr@users.noreply.github.com> Date: Tue, 19 May 2026 20:43:21 -0500 Subject: [PATCH 07/12] Digital Sensor commit, basic tasks raw State and then isTriggered based of polarity --- .../hardware/sensors/NextDigitalSensor.kt | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 hardware/src/main/kotlin/dev/nextftc/hardware/sensors/NextDigitalSensor.kt diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/sensors/NextDigitalSensor.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/sensors/NextDigitalSensor.kt new file mode 100644 index 0000000..4b59821 --- /dev/null +++ b/hardware/src/main/kotlin/dev/nextftc/hardware/sensors/NextDigitalSensor.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2026 NextFTC Team + * + * Use of this source code is governed by an BSD-3-clause + * license that can be found in the LICENSE.md file at the root of this repository or at + * https://opensource.org/license/bsd-3-clause. + */ + +package dev.nextftc.hardware.sensors + +import com.qualcomm.robotcore.hardware.DigitalChannel +import dev.nextftc.hardware.LazyHardware +import dev.nextftc.hardware.RobotController + +/** + * Lightweight wrapper around a [DigitalChannel] for reading digital sensors + * like limit switches, magnetic switches, and beam breaks. + * + * Most digital sensors are "active low" — they read `false` when triggered + * (switch pressed, magnet present, beam broken) and `true` when idle. This + * wrapper handles that inversion via [triggeredOnLow] so [isTriggered] always + * means what you'd expect. + * + * Example: + * ``` + * val beamBreak = NextDigitalSensor("beamBreak") + * if (beamBreak.isTriggered) { stopMotor() } + *``` + * + * @param initializer Lazily resolves the backing [DigitalChannel]. + * @param triggeredOnLow If true, [isTriggered] returns the inverse of the raw + * sensor state. Defaults to true (matches most FTC digital sensors). + * + * @author 28shettr + */ +class NextDigitalSensor( + initializer: () -> DigitalChannel, + private val triggeredOnLow: Boolean = true, +) { + @JvmOverloads + constructor(name: String, activeLow: Boolean = true) : this( + { + RobotController.hardwareMap[name] as DigitalChannel + }, + activeLow, + ) + + private val sensor by LazyHardware(initializer).also { + it.applyAfterInit { channel -> channel.mode = DigitalChannel.Mode.INPUT } + } + + /** Raw state of the digital channel*/ + val rawState: Boolean + get() = sensor.state + + /** True if the sensor is currently triggered (accounting for [triggeredOnLow]). */ + val isTriggered: Boolean + get() = if (triggeredOnLow) !sensor.state + else sensor.state +} \ No newline at end of file From 5f2d3f7dbee3fccb0d2fe78c49778842bb549cc6 Mon Sep 17 00:00:00 2001 From: Rohit Shetty <138729620+28shettr@users.noreply.github.com> Date: Thu, 21 May 2026 18:26:07 -0500 Subject: [PATCH 08/12] spotless apply done --- .../hardware/sensors/NextDigitalSensor.kt | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/sensors/NextDigitalSensor.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/sensors/NextDigitalSensor.kt index 4b59821..7fb0d9d 100644 --- a/hardware/src/main/kotlin/dev/nextftc/hardware/sensors/NextDigitalSensor.kt +++ b/hardware/src/main/kotlin/dev/nextftc/hardware/sensors/NextDigitalSensor.kt @@ -33,28 +33,28 @@ import dev.nextftc.hardware.RobotController * * @author 28shettr */ -class NextDigitalSensor( - initializer: () -> DigitalChannel, - private val triggeredOnLow: Boolean = true, -) { - @JvmOverloads - constructor(name: String, activeLow: Boolean = true) : this( - { - RobotController.hardwareMap[name] as DigitalChannel - }, - activeLow, - ) +class NextDigitalSensor(initializer: () -> DigitalChannel, private val triggeredOnLow: Boolean = true) { + @JvmOverloads + constructor(name: String, activeLow: Boolean = true) : this( + { + RobotController.hardwareMap[name] as DigitalChannel + }, + activeLow, + ) - private val sensor by LazyHardware(initializer).also { - it.applyAfterInit { channel -> channel.mode = DigitalChannel.Mode.INPUT } - } + private val sensor by LazyHardware(initializer).also { + it.applyAfterInit { channel -> channel.mode = DigitalChannel.Mode.INPUT } + } - /** Raw state of the digital channel*/ - val rawState: Boolean - get() = sensor.state + /** Raw state of the digital channel*/ + val rawState: Boolean + get() = sensor.state - /** True if the sensor is currently triggered (accounting for [triggeredOnLow]). */ - val isTriggered: Boolean - get() = if (triggeredOnLow) !sensor.state - else sensor.state -} \ No newline at end of file + /** True if the sensor is currently triggered (accounting for [triggeredOnLow]). */ + val isTriggered: Boolean + get() = if (triggeredOnLow) { + !sensor.state + } else { + sensor.state + } +} From 3c3e1ca275349c7b2febd32d527182cb87938eb9 Mon Sep 17 00:00:00 2001 From: Rohit Shetty <138729620+28shettr@users.noreply.github.com> Date: Thu, 21 May 2026 18:57:14 -0500 Subject: [PATCH 09/12] commit --- .idea/codeStyles/Project.xml | 123 +++++++++++++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 1 + .idea/gradle.xml | 2 + .idea/migrations.xml | 10 +++ 4 files changed, 136 insertions(+) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/migrations.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..7643783 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,123 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index a55e7a1..6e6eec1 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,6 @@ + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 732832c..07468d9 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -4,7 +4,9 @@