diff --git a/astro.config.mjs b/astro.config.mjs
index 9f5ca58..62bb0c0 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -66,8 +66,10 @@ export default defineConfig({
{ slug: "robot/mechanisms" },
{ slug: "robot/nextrobot" },
{ slug: "robot/nextopmode" },
+ { slug: "robot/command-gamepad" },
{ slug: "robot/triggers" },
{ slug: "robot/drive-commands" },
+ { slug: "robot/telemetry" },
{ slug: "robot/project-structure" },
{
label: "Advanced",
@@ -102,6 +104,12 @@ export default defineConfig({
icon: "document",
items: [{ autogenerate: { directory: "control" } }],
},
+ {
+ label: "References",
+ link: "/references/example-repos",
+ icon: "open-book",
+ items: [{ autogenerate: { directory: "references" } }],
+ },
]),
starlightThemeGalaxy(),
starlightLinksValidator(),
diff --git a/src/content/docs/hardware/actuators/feedback-servos.mdx b/src/content/docs/hardware/actuators/feedback-servos.mdx
index 3f7e8ee..32af9fb 100644
--- a/src/content/docs/hardware/actuators/feedback-servos.mdx
+++ b/src/content/docs/hardware/actuators/feedback-servos.mdx
@@ -21,7 +21,7 @@ All of these below are interchangeable with `NextFeedbackCRServo`.
val armServo = NextFeedbackServo("armServo", NextAnalogInput("armEncoder"))
// By Lynx Module and port
-val armServo = NextFeedbackServo(RobotController.expansionHub, 0, NextAnalogInput(RobotController.expansionHub, 0))
+val armServo = NextFeedbackServo(RobotController.controlHub, 0, NextAnalogInput(RobotController.controlHub, 0))
```
@@ -32,7 +32,7 @@ val armServo = NextFeedbackServo(RobotController.expansionHub, 0, NextAnalogInpu
NextFeedbackServo armServo = new NextFeedbackServo("armServo", new NextAnalogInput("armEncoder"));
// By Lynx Module and port
-NextFeedbackServo armServo = new NextFeedbackServo(RobotController.getExpansionHub(), 0, new NextAnalogInput(RobotController.getExpansionHub(), 0));
+NextFeedbackServo armServo = new NextFeedbackServo(RobotController.controlHub(), 0, new NextAnalogInput(RobotController.controlHub(), 0));
```
diff --git a/src/content/docs/references/example-repos.mdx b/src/content/docs/references/example-repos.mdx
new file mode 100644
index 0000000..d2db553
--- /dev/null
+++ b/src/content/docs/references/example-repos.mdx
@@ -0,0 +1,24 @@
+---
+title: Example Repos
+---
+
+Here are some real robot projects built with NextFTC v2.
+
+## BIOBUZZ
+
+- [**BIOBUZZ JAVA**](https://github.com/13201Hazmat/BioBuzz-RI30h): Full robot code from RI30h's BIOBUZZ robot.
+
+## DECODE
+
+- [**DECODE JAVA**](https://github.com/28shettr/NextFTCv2_Decode): Full robot code from the DECODE season.
+
+## INTO THE DEEP
+
+- [**ITD JAVA**](https://github.com/KavinSankaran/ITD-NextFTCV2-Example): Full robot code from INTO THE DEEP.
+
+:::tip
+One of the best ways to learn NextFTC and code in general is to read other people`s code.
+Look at how each repo sets up code.
+If you want to use something, type it out yourself instead of copy-pasting.
+You'll learn a lot more that way.
+:::
diff --git a/src/content/docs/robot/command-gamepad.mdx b/src/content/docs/robot/command-gamepad.mdx
new file mode 100644
index 0000000..9d0a976
--- /dev/null
+++ b/src/content/docs/robot/command-gamepad.mdx
@@ -0,0 +1,79 @@
+---
+title: Command Gamepad
+description: Mapping gamepad buttons to Triggers with CommandGamepad
+sidebar:
+ order: 5
+---
+
+import { Tabs, TabItem } from "@astrojs/starlight/components";
+
+`CommandGamepad` wraps the standard FTC `Gamepad` and exposes its buttons and sticks as command-based `Trigger`s.
+This lets you map controller inputs directly to commands using the WPILib-style Trigger API.
+
+## Creating a CommandGamepad
+
+
+
+
+```kotlin
+val gp1 = CommandGamepad(gamepad1)
+```
+
+
+
+
+```java
+CommandGamepad gp1 = new CommandGamepad(gamepad1);
+```
+
+
+
+
+`CommandGamepad` accepts an optional `eventLoop` parameter, defaulting to `Trigger.defaultEventLoop`.
+You generally won't need to override it unless you're managing multiple event loops.
+
+## Usage
+
+Every `button` is exposed as a `Trigger`, which you can bind to commands with methods like `onTrue()` or `onFalse()` etc.
+
+
+
+
+```kotlin
+gp1.circle.onTrue(robot.intake.on())
+gp1.circle.onFalse(robot.intake.off())
+```
+
+
+
+
+```java
+gp1.circle().onTrue(robot.intake.on());
+gp1.circle().onFalse(robot.intake.off());
+
+```
+
+
+
+
+### Sticks and Triggers
+
+Joysticks and gamepad triggers are exposed as `RangeTrigger`s instead of regular `Trigger`s,
+since they report an analog value rather than a boolean value, letting you
+
+
+
+
+```kotlin
+gp1.rightTrigger.isOver(0.2).onTrue(robot.launcher.launch())
+```
+
+
+
+
+```java
+gp1.rightTrigger().isOver(0.2).onTrue(robot.launcher.launch());
+```
+
+
+
diff --git a/src/content/docs/robot/telemetry.mdx b/src/content/docs/robot/telemetry.mdx
new file mode 100644
index 0000000..1cbec42
--- /dev/null
+++ b/src/content/docs/robot/telemetry.mdx
@@ -0,0 +1,113 @@
+---
+title: Telemetry
+description: Broadcasting telemetry to multiple destinations with Telemetry
+sidebar:
+ order: 8
+---
+
+import { Tabs, TabItem } from "@astrojs/starlight/components";
+
+`Telemetry` lets you send data to the Driver Station, FTC Dashboard, or anywhere else, all at once.
+The `Telemetry` functions are all static; you don't need to create your own telemetry instances.
+By default, everything goes to the Driver Station.
+If you want it to also show up somewhere else, register another backend.
+
+## Usage
+
+### `log(key, value)` / `log(line)`
+
+Logs a data pair, or a raw string, to every registered backend.
+
+
+
+
+```kotlin
+Telemetry.log("Arm Position", arm.position)
+Telemetry.log("Ready")
+```
+
+
+
+
+```java
+Telemetry.log("Arm Position", arm.getPosition());
+Telemetry.log("Ready");
+```
+
+
+
+
+### `update()`
+
+Sends everything you've logged out to all your backends. `TelemetryHook` already calls this for you every loop, so you usually won't need to call it yourself.
+
+
+
+
+```kotlin
+Telemetry.update()
+```
+
+
+
+
+```java
+Telemetry.update();
+```
+
+
+
+
+### `addBackend(backend)`
+
+Adds another place for your telemetry to go.
+You can pass in a `TelemetryBackend`, or a regular FTC SDK `Telemetry` instance (like the one from FTC Dashboard) and it'll be wrapped for you automatically.
+
+
+
+
+```kotlin
+Telemetry.addBackend(FtcDashboard.getInstance().telemetry)
+```
+
+
+
+
+```java
+Telemetry.addBackend(FtcDashboard.getInstance().getTelemetry());
+```
+
+
+
+
+## Logging with FateWeaver
+
+[FateWeaver](https://github.com/HermesFTC/FateWeaver#usage) is a RoadRunner-log based data logging library for FTC.
+Its `FlightRecorder` implements the standard FTC SDK `Telemetry` interface, so you can register it directly as a backend using the `addBackend(SdkTelemetry)` overload
+
+
+
+
+```kotlin
+Telemetry.addBackend(FlightRecorder)
+```
+
+
+
+
+```java
+Telemetry.addBackend(FlightRecorder.INSTANCE);
+```
+
+
+
+
+Once registered, every `Telemetry.log()` call also gets written to your FateWeaver logs, which is viewable and downloadable from the robot's web dashboard using either [`http://192.168.43.1:8080/fate`](http://192.168.43.1:8080/fate) (Control Hub) or [`http://192.168.49.1:8080/fate`](http://192.168.49.1:8080/fate) (RC phone), and openable in [AdvantageScope](https://docs.advantagescope.org/overview/installation/).
+
+A couple of things to know about logging through `Telemetry` this way:
+
+- All data is logged as strings, to stay compatible with `Telemetry`.
+- Each new caption you log automatically creates its own log channel in FateWeaver.
+
+If you want typed channels, custom schemas, or other FateWeaver-specific features (`FlightRecorder.createChannel()`, `CustomStructSchema`, etc.), use the FateWeaver API directly.
+See the [FateWeaver documentation](https://github.com/HermesFTC/FateWeaver#usage) for details.