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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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 6a3dc735c6af15bde6d9c5bda9fd0281900976a6 Mon Sep 17 00:00:00 2001
From: Rohit Shetty <138729620+28shettr@users.noreply.github.com>
Date: Thu, 21 May 2026 19:31:57 -0500
Subject: [PATCH 07/20] Limelight Wrapper created
---
.../nextftc/hardware/webcams/NextLimelight.kt | 63 +++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
new file mode 100644
index 0000000..7ea5880
--- /dev/null
+++ b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.webcams
+
+import com.qualcomm.hardware.limelightvision.LLResult
+import com.qualcomm.hardware.limelightvision.Limelight3A
+import dev.nextftc.hardware.LazyHardware
+import dev.nextftc.hardware.RobotController
+
+/**
+ * A NextFTC wrapper around the [Limelight3A] vision sensor that resolves the device
+ * lazily from the hardware map, so you never have to fetch it yourself.
+ *
+ * Wraps the common lifecycle methods directly; anything else on the underlying
+ * sensor is reachable through [camera].
+ *
+ * @param initializer supplies the underlying [Limelight3A] when first accessed.
+ */
+class NextLimelight(initializer: () -> Limelight3A) {
+ constructor(name: String) : this(
+ { RobotController.hardwareMap[name] as Limelight3A },
+ )
+
+ private val limelight by LazyHardware(initializer)
+
+ /** The underlying [Limelight3A], for anything not wrapped. */
+ val camera: Limelight3A get() = limelight
+
+ /** The most recent result from the polling loop. */
+ val latestResult: LLResult get() = limelight.latestResult
+
+ /** True while the polling loop is active. */
+ val isRunning: Boolean get() = limelight.isRunning
+
+ /** True if the sensor has reported data within the last ~250ms. */
+ val isConnected: Boolean get() = limelight.isConnected
+
+ /** Starts the polling loop. */
+ fun start() = limelight.start()
+
+ /** Stops the polling loop. */
+ fun stop() = limelight.stop()
+
+ /** Switches to the pipeline at the given index. */
+ fun setPipeline(pipeline: Int) = limelight.pipelineSwitch(pipeline)
+
+ /** Sets the poll rate in Hz; must be called before [start]. */
+ fun setPollRate(hz: Int) = limelight.setPollRateHz(hz)
+
+ /** Sets the poll rate and pipeline, then starts polling in one call. */
+ @JvmOverloads
+ fun startReading(pipeline: Int, hz: Int = 100) {
+ setPollRate(hz)
+ setPipeline(pipeline)
+ start()
+ }
+}
From 8be907714446ba0cf7291c551d973055549155f5 Mon Sep 17 00:00:00 2001
From: Rohit Shetty <138729620+28shettr@users.noreply.github.com>
Date: Thu, 21 May 2026 20:52:23 -0500
Subject: [PATCH 08/20] Husky Lens Wrapper created
---
.../nextftc/hardware/webcams/NextHuskyLens.kt | 51 +++++++++++++++++++
1 file changed, 51 insertions(+)
create mode 100644 hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextHuskyLens.kt
diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextHuskyLens.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextHuskyLens.kt
new file mode 100644
index 0000000..7a87578
--- /dev/null
+++ b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextHuskyLens.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.webcams
+
+import com.qualcomm.hardware.dfrobot.HuskyLens
+import dev.nextftc.hardware.LazyHardware
+import dev.nextftc.hardware.RobotController
+
+/**
+ * A NextFTC wrapper around the [HuskyLens] vision sensor that resolves the device
+ * lazily from the hardware map, so you never have to fetch it yourself.
+ *
+ * Wraps the common read and setup methods directly; anything else on the underlying
+ * sensor is reachable through [camera].
+ *
+ * @param initializer supplies the underlying [HuskyLens] when first accessed.
+ */
+class NextHuskyLens(initializer: () -> HuskyLens) {
+ constructor(name: String) : this(
+ { RobotController.hardwareMap[name] as HuskyLens },
+ )
+
+ private val huskyLens by LazyHardware(initializer)
+
+ /** The underlying [HuskyLens], for anything not wrapped here. */
+ val camera: HuskyLens get() = huskyLens
+
+ /** Verifies the device is responding over I2C. */
+ fun knock(): Boolean = huskyLens.knock()
+
+ /** Selects the recognition algorithm; call once on startup. */
+ fun selectAlgorithm(algorithm: HuskyLens.Algorithm) = huskyLens.selectAlgorithm(algorithm)
+
+ /** Returns all currently seen blocks, capped at 6. */
+ fun blocks(): Array = huskyLens.blocks()
+
+ /** Returns seen blocks with the given id, capped at 6. */
+ fun blocks(id: Int): Array = huskyLens.blocks(id)
+
+ /** Returns all currently seen arrows, capped at 6. */
+ fun arrows(): Array = huskyLens.arrows()
+
+ /** Returns seen arrows with the given id, capped at 6. */
+ fun arrows(id: Int): Array = huskyLens.arrows(id)
+}
From 98eabfb6040931bf9bfc8610d8def1054e3199a2 Mon Sep 17 00:00:00 2001
From: Rohit Shetty <138729620+28shettr@users.noreply.github.com>
Date: Sat, 23 May 2026 09:20:54 -0500
Subject: [PATCH 09/20] working on it
---
.../nextftc/hardware/webcams/NextLimelight.kt | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
index 7ea5880..b5f9ccb 100644
--- a/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
+++ b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
@@ -12,6 +12,11 @@ import com.qualcomm.hardware.limelightvision.LLResult
import com.qualcomm.hardware.limelightvision.Limelight3A
import dev.nextftc.hardware.LazyHardware
import dev.nextftc.hardware.RobotController
+import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit
+import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit
+import org.firstinspires.ftc.robotcore.external.navigation.Pose3D
+
+
/**
* A NextFTC wrapper around the [Limelight3A] vision sensor that resolves the device
@@ -41,6 +46,11 @@ class NextLimelight(initializer: () -> Limelight3A) {
/** True if the sensor has reported data within the last ~250ms. */
val isConnected: Boolean get() = limelight.isConnected
+ val tX: Double get() = latestResult.tx
+
+ val tY: Double get() = latestResult.ty
+
+
/** Starts the polling loop. */
fun start() = limelight.start()
@@ -53,6 +63,21 @@ class NextLimelight(initializer: () -> Limelight3A) {
/** Sets the poll rate in Hz; must be called before [start]. */
fun setPollRate(hz: Int) = limelight.setPollRateHz(hz)
+ fun recalibrate(){
+ val botpose: Pose3D = latestResult.botpose ?: return
+
+ val rawX = botpose.getPosition().x
+ val rawY = botpose.getPosition().y
+ val inchX: Double = rawY / DistanceUnit.mPerInch
+ val inchY: Double = -(rawX) / DistanceUnit.mPerInch
+ val heading = botpose.getOrientation().getYaw(AngleUnit.DEGREES) - 90
+
+
+ val pedroX = inchX + 72
+ val pedroY = inchY + 72
+// val pedroPose: Pose3D = Pose3D(pedroX, pedroY, Math.toRadians(heading))
+ }
+
/** Sets the poll rate and pipeline, then starts polling in one call. */
@JvmOverloads
fun startReading(pipeline: Int, hz: Int = 100) {
From 4e73a788ed9a83a25b511f774739cd2d94804b95 Mon Sep 17 00:00:00 2001
From: Rohit Shetty <138729620+28shettr@users.noreply.github.com>
Date: Sat, 23 May 2026 09:21:03 -0500
Subject: [PATCH 10/20] working on it
---
.idea/caches/deviceStreaming.xml | 1785 ++++++++++++++++++++++++++
.idea/codeStyles/Project.xml | 123 ++
.idea/codeStyles/codeStyleConfig.xml | 1 +
.idea/dictionaries/project.xml | 7 +
.idea/gradle.xml | 2 +
.idea/migrations.xml | 10 +
6 files changed, 1928 insertions(+)
create mode 100644 .idea/caches/deviceStreaming.xml
create mode 100644 .idea/codeStyles/Project.xml
create mode 100644 .idea/dictionaries/project.xml
create mode 100644 .idea/migrations.xml
diff --git a/.idea/caches/deviceStreaming.xml b/.idea/caches/deviceStreaming.xml
new file mode 100644
index 0000000..f8d0743
--- /dev/null
+++ b/.idea/caches/deviceStreaming.xml
@@ -0,0 +1,1785 @@
+
+
+
+
+
+
+
\ 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/dictionaries/project.xml b/.idea/dictionaries/project.xml
new file mode 100644
index 0000000..69ffc71
--- /dev/null
+++ b/.idea/dictionaries/project.xml
@@ -0,0 +1,7 @@
+
+
+
+ intializer
+
+
+
\ 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 @@
+
+
\ No newline at end of file
From d76b65ba0506d38da2b437c71642f015d2a78993 Mon Sep 17 00:00:00 2001
From: Rohit Shetty <138729620+28shettr@users.noreply.github.com>
Date: Sat, 23 May 2026 09:24:55 -0500
Subject: [PATCH 11/20] x
---
.idea/markdown.xml | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 .idea/markdown.xml
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
From fd8ea52f0157167b83a085b86212a8cfd1f322dd Mon Sep 17 00:00:00 2001
From: Rohit Shetty <138729620+28shettr@users.noreply.github.com>
Date: Sat, 23 May 2026 09:48:47 -0500
Subject: [PATCH 12/20] working
---
.../nextftc/hardware/webcams/NextLimelight.kt | 47 ++++++++++++++-----
1 file changed, 35 insertions(+), 12 deletions(-)
diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
index b5f9ccb..2e52545 100644
--- a/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
+++ b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
@@ -9,13 +9,16 @@
package dev.nextftc.hardware.webcams
import com.qualcomm.hardware.limelightvision.LLResult
+import com.qualcomm.hardware.limelightvision.LLResultTypes
import com.qualcomm.hardware.limelightvision.Limelight3A
+import dev.nextftc.control.geometry.Pose2d
import dev.nextftc.hardware.LazyHardware
import dev.nextftc.hardware.RobotController
import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit
import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit
import org.firstinspires.ftc.robotcore.external.navigation.Pose3D
-
+import kotlin.math.pow
+import kotlin.math.sqrt
/**
@@ -50,7 +53,6 @@ class NextLimelight(initializer: () -> Limelight3A) {
val tY: Double get() = latestResult.ty
-
/** Starts the polling loop. */
fun start() = limelight.start()
@@ -63,19 +65,40 @@ class NextLimelight(initializer: () -> Limelight3A) {
/** Sets the poll rate in Hz; must be called before [start]. */
fun setPollRate(hz: Int) = limelight.setPollRateHz(hz)
- fun recalibrate(){
- val botpose: Pose3D = latestResult.botpose ?: return
+ /** Returns the robot's field position from the latest Limelight result in Pedro coordinates, or `null` if no valid pose is available. */
+ fun getPedroPoseFromLimelight(): Pose2d? {
+ val result = limelight.getLatestResult()
+ if (result == null || !result.isValid()) return null
+
+ val botpose = result.botpose ?: return null
+
+ val rawX = botpose.getPosition().x
+ val rawY = botpose.getPosition().y
+ val inchX = rawY / DistanceUnit.mPerInch
+ val inchY = -(rawX) / DistanceUnit.mPerInch
+ val heading = botpose.getOrientation().getYaw(AngleUnit.DEGREES) - 90
+
+ val pedroX = inchX + 72
+ val pedroY = inchY + 72
+ val pedroPose = Pose2d(pedroX, pedroY, Math.toRadians(heading))
+
+ return pedroPose
+ }
- val rawX = botpose.getPosition().x
- val rawY = botpose.getPosition().y
- val inchX: Double = rawY / DistanceUnit.mPerInch
- val inchY: Double = -(rawX) / DistanceUnit.mPerInch
- val heading = botpose.getOrientation().getYaw(AngleUnit.DEGREES) - 90
+ fun getDistance(): Double{
+ val tags: List = latestResult.fiducialResults
+ var distance: Double
+ for (tag in tags) {
+ val pose: Pose3D = tag.robotPoseTargetSpace
+ distance = sqrt(
+ pose.getPosition().x.pow(2.0) +
+ pose.getPosition().y.pow(2.0) +
+ pose.getPosition().z.pow(2.0)
+ )
+ }
+ return distance
- val pedroX = inchX + 72
- val pedroY = inchY + 72
-// val pedroPose: Pose3D = Pose3D(pedroX, pedroY, Math.toRadians(heading))
}
/** Sets the poll rate and pipeline, then starts polling in one call. */
From ec6aea6de7342e086e50033554307747bebdf8f2 Mon Sep 17 00:00:00 2001
From: Rohit Shetty <138729620+28shettr@users.noreply.github.com>
Date: Sat, 23 May 2026 10:09:31 -0500
Subject: [PATCH 13/20] added limelight recalibrating and distance math
---
.../nextftc/hardware/webcams/NextLimelight.kt | 35 ++++++++++---------
1 file changed, 18 insertions(+), 17 deletions(-)
diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
index 2e52545..7e781a1 100644
--- a/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
+++ b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
@@ -20,7 +20,6 @@ import org.firstinspires.ftc.robotcore.external.navigation.Pose3D
import kotlin.math.pow
import kotlin.math.sqrt
-
/**
* A NextFTC wrapper around the [Limelight3A] vision sensor that resolves the device
* lazily from the hardware map, so you never have to fetch it yourself.
@@ -65,6 +64,24 @@ class NextLimelight(initializer: () -> Limelight3A) {
/** Sets the poll rate in Hz; must be called before [start]. */
fun setPollRate(hz: Int) = limelight.setPollRateHz(hz)
+ var distance: Double = 0.0
+
+ /** Returns the straight-line distance from the robot to the AprilTag matching [id] (or any visible tag if [id] is null) in the given [unit]. */
+ fun getDistance(unit: DistanceUnit, id: Int? = null): Double {
+ val tags: List = latestResult.fiducialResults
+ for (tag in tags) {
+ if (id == null || tag.fiducialId == id) {
+ val pose: Pose3D = tag.robotPoseTargetSpace
+ distance = sqrt(
+ pose.position.x.pow(2.0) +
+ pose.position.y.pow(2.0) +
+ pose.position.z.pow(2.0),
+ )
+ }
+ }
+ return unit.fromUnit(DistanceUnit.METER, distance)
+ }
+
/** Returns the robot's field position from the latest Limelight result in Pedro coordinates, or `null` if no valid pose is available. */
fun getPedroPoseFromLimelight(): Pose2d? {
val result = limelight.getLatestResult()
@@ -85,22 +102,6 @@ class NextLimelight(initializer: () -> Limelight3A) {
return pedroPose
}
- fun getDistance(): Double{
- val tags: List = latestResult.fiducialResults
- var distance: Double
- for (tag in tags) {
- val pose: Pose3D = tag.robotPoseTargetSpace
- distance = sqrt(
- pose.getPosition().x.pow(2.0) +
- pose.getPosition().y.pow(2.0) +
- pose.getPosition().z.pow(2.0)
- )
- }
-
- return distance
-
- }
-
/** Sets the poll rate and pipeline, then starts polling in one call. */
@JvmOverloads
fun startReading(pipeline: Int, hz: Int = 100) {
From f624bdf296f1c2f4052857c1860dcf314b3712d8 Mon Sep 17 00:00:00 2001
From: Rohit Shetty <138729620+28shettr@users.noreply.github.com>
Date: Sat, 23 May 2026 10:18:25 -0500
Subject: [PATCH 14/20] updated kdoc
---
.../main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
index 7e781a1..993520e 100644
--- a/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
+++ b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
@@ -48,8 +48,10 @@ class NextLimelight(initializer: () -> Limelight3A) {
/** True if the sensor has reported data within the last ~250ms. */
val isConnected: Boolean get() = limelight.isConnected
+ /** The horizontal offset angle, in degrees, from the camera's crosshair to the target. */
val tX: Double get() = latestResult.tx
+ /** The vertical offset angle, in degrees, from the camera's crosshair to the target. */
val tY: Double get() = latestResult.ty
/** Starts the polling loop. */
@@ -66,7 +68,7 @@ class NextLimelight(initializer: () -> Limelight3A) {
var distance: Double = 0.0
- /** Returns the straight-line distance from the robot to the AprilTag matching [id] (or any visible tag if [id] is null) in the given [unit]. */
+ /** Returns the straight-line distance (hypotenuse) from the robot to the AprilTag matching [id] (or any visible tag if [id] is null) in the given [unit]. */
fun getDistance(unit: DistanceUnit, id: Int? = null): Double {
val tags: List = latestResult.fiducialResults
for (tag in tags) {
From 83aeb2cff33c904ae6ca9b4669bf1c7a17775143 Mon Sep 17 00:00:00 2001
From: Rohit Shetty <138729620+28shettr@users.noreply.github.com>
Date: Sat, 23 May 2026 10:20:06 -0500
Subject: [PATCH 15/20] forgot to add jvmoverload
---
.../main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt | 1 +
1 file changed, 1 insertion(+)
diff --git a/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
index 993520e..9b914ee 100644
--- a/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
+++ b/hardware/src/main/kotlin/dev/nextftc/hardware/webcams/NextLimelight.kt
@@ -69,6 +69,7 @@ class NextLimelight(initializer: () -> Limelight3A) {
var distance: Double = 0.0
/** Returns the straight-line distance (hypotenuse) from the robot to the AprilTag matching [id] (or any visible tag if [id] is null) in the given [unit]. */
+ @JvmOverloads
fun getDistance(unit: DistanceUnit, id: Int? = null): Double {
val tags: List = latestResult.fiducialResults
for (tag in tags) {
From eb28c5655f135b8d302e34feca345a6efd0362a5 Mon Sep 17 00:00:00 2001
From: Rohit Shetty <138729620+28shettr@users.noreply.github.com>
Date: Wed, 27 May 2026 20:33:38 -0500
Subject: [PATCH 16/20] merged
---
.idea/caches/deviceStreaming.xml | 36 +++++++++++++++++++
.../nextftc/hardware/webcams/NextHuskyLens.kt | 5 +++
.../nextftc/hardware/webcams/NextLimelight.kt | 3 ++
3 files changed, 44 insertions(+)
diff --git a/.idea/caches/deviceStreaming.xml b/.idea/caches/deviceStreaming.xml
index 2d334f0..f432cf1 100644
--- a/.idea/caches/deviceStreaming.xml
+++ b/.idea/caches/deviceStreaming.xml
@@ -677,6 +677,18 @@