Skip to content

Commit

Permalink
Added Controllers Page (#177)
Browse files Browse the repository at this point in the history
![image](https://github.com/user-attachments/assets/3a1fc8a9-b3d6-45e3-8ea6-16db9e9a2e5d)

Useful to debugging issues with the controller itself or the Dashboard's code for reading it

---------

Co-authored-by: Levi Lesches <[email protected]>
  • Loading branch information
Gold872 and Levi-Lesches authored Nov 19, 2024
1 parent f3281a4 commit 14066ef
Show file tree
Hide file tree
Showing 15 changed files with 433 additions and 112 deletions.
Binary file added assets/gamesir_controller.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions lib/pages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class Routes {

/// The name of the logs page.
static const String logs = "Logs";

/// The name of the controllers page
static const String controllers = "Controllers";

/// The name of the rocks page.
static const String rocks = "Rocks";
Expand Down
1 change: 1 addition & 0 deletions lib/src/models/rover/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,6 @@ class Controller extends Model {
// print(message.toProto3Json());
models.messages.sendMessage(message);
}
notifyListeners();
}
}
14 changes: 7 additions & 7 deletions lib/src/models/rover/controls/arm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,21 @@ class ArmControls extends RoverControls {
@override
List<Message> parseInputs(GamepadState state) => [
// Manual control
if (state.normalRightX.abs() > state.normalRightY.abs() && state.normalRightX != 0)
if (state.normalRightX.abs() > state.normalRightJoystickY.abs() && state.normalRightX != 0)
ArmCommand(swivel: MotorCommand(moveRadians: state.normalRightX * settings.swivel)),
if (state.normalRightY.abs() > state.normalRightX.abs() && state.normalRightY != 0)
ArmCommand(shoulder: MotorCommand(moveRadians: state.normalRightY * settings.shoulder)),
if (state.normalRightJoystickY.abs() > state.normalRightX.abs() && state.normalRightJoystickY != 0)
ArmCommand(shoulder: MotorCommand(moveRadians: state.normalRightJoystickY * settings.shoulder)),
if (state.normalLeftY != 0) ArmCommand(elbow: MotorCommand(moveRadians: state.normalLeftY * settings.elbow)),
// The bumpers should be pseudo-IK: Move the shoulder and elbow in sync.
if (state.normalShoulder != 0) ArmCommand(
shoulder: MotorCommand(moveRadians: state.normalShoulder * settings.shoulder * -1),
elbow: MotorCommand(moveRadians: state.normalShoulder * settings.elbow),
if (state.normalShoulders != 0) ArmCommand(
shoulder: MotorCommand(moveRadians: state.normalShoulders * settings.shoulder * -1),
elbow: MotorCommand(moveRadians: state.normalShoulders * settings.elbow),
),

// Gripper
if (state.normalDpadY != 0) GripperCommand(lift: MotorCommand(moveRadians: state.normalDpadY * settings.lift)),
if (state.normalDpadX != 0) GripperCommand(rotate: MotorCommand(moveRadians: state.normalDpadX * settings.rotate)),
if (state.normalTrigger != 0) GripperCommand(pinch: MotorCommand(moveRadians: state.normalTrigger * settings.pinch)),
if (state.normalTriggers != 0) GripperCommand(pinch: MotorCommand(moveRadians: state.normalTriggers * settings.pinch)),

// Custom actions
if (state.buttonA && !isAPressed) () { isAPressed = true; return GripperCommand(open: true); }(),
Expand Down
2 changes: 1 addition & 1 deletion lib/src/models/rover/controls/camera.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class CameraControls extends RoverControls {
final newFrontSwivel = state.normalLeftX;
final newFrontTilt = state.normalLeftY;
final newRearSwivel = state.normalRightX;
final newRearTilt = -1 * state.normalRightY;
final newRearTilt = -1 * state.normalRightJoystickY;
if (newFrontSwivel.abs() >= 0.05 || newFrontTilt.abs() >= 0.05) {
// Update the front camera. Now, choose which axis
if (newFrontSwivel.abs() > newFrontTilt.abs()) {
Expand Down
4 changes: 2 additions & 2 deletions lib/src/models/rover/controls/modern_drive.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class ModernDriveControls extends RoverControls {

/// Gets all commands for the wheels based on the gamepad state.
List<DriveCommand> getWheelCommands(GamepadState state) {
final speed = state.normalTrigger; // sum of both triggers, [-1, 1]
final speed = state.normalTriggers; // sum of both triggers, [-1, 1]
if (speed == 0) {
final left = state.normalLeftX;
final right = state.normalLeftX;
Expand Down Expand Up @@ -99,7 +99,7 @@ class ModernDriveControls extends RoverControls {
final newFrontSwivel = state.normalDpadX;
final newFrontTilt = state.normalDpadY;
final newRearSwivel = state.normalRightX;
final newRearTilt = state.normalRightY;
final newRearTilt = state.normalRightJoystickY;
if (newFrontSwivel.abs() >= 0.05 || newFrontTilt.abs() >= 0.05) {
// Update the front camera. Now, choose which axis
if (newFrontSwivel.abs() > newFrontTilt.abs()) {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/models/rover/controls/tank_drive.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class DriveControls extends RoverControls {
List<Message> parseInputs(GamepadState state) => [
DriveCommand(throttle: throttle, setThrottle: true),
DriveCommand(setLeft: true, left: state.normalLeftY),
DriveCommand(setRight: true, right: -1*state.normalRightY),
DriveCommand(setRight: true, right: -1*state.normalRightJoystickY),
];

@override
Expand Down
90 changes: 90 additions & 0 deletions lib/src/pages/controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import "package:flutter/material.dart";
import "package:rover_dashboard/models.dart";
import "package:rover_dashboard/widgets.dart";

import "controllers/controller.dart";

/// A view model to select and listen to a gamepad.
class ControllersViewModel with ChangeNotifier {
/// The gamepad to listen to.
Controller selectedController = models.rover.controller1;

/// Starts listening to the gamepad.
ControllersViewModel() {
selectedController.addListener(notifyListeners);
}

@override
void dispose() {
selectedController.removeListener(notifyListeners);
super.dispose();
}

/// Changes which controller is being listened to.
void setController(Controller? value) {
if (value == null) return;
selectedController.removeListener(notifyListeners);
selectedController = value;
selectedController.addListener(notifyListeners);
notifyListeners();
}
}

/// The UI Page to display the controller status
class ControllersPage extends ReactiveWidget<ControllersViewModel> {
/// The index of this view.
final int index;

/// Const constructor for [ControllersPage]
const ControllersPage({required this.index, super.key});

@override
ControllersViewModel createModel() => ControllersViewModel();

@override
Widget build(BuildContext context, ControllersViewModel model) => Column(
children: [
const SizedBox(height: 16),
Row(
children: [
const Spacer(),
const Text("Controller: "),
DropdownButton<Controller>(
value: model.selectedController,
onChanged: model.setController,
items: [
DropdownMenuItem(
value: models.rover.controller1,
child: const Text("Controller 1"),
),
DropdownMenuItem(
value: models.rover.controller2,
child: const Text("Controller 2"),
),
DropdownMenuItem(
value: models.rover.controller3,
child: const Text("Controller 3"),
),
],
),
const SizedBox(width: 8),
FilledButton(
onPressed: model.selectedController.isConnected
? model.selectedController.gamepad.pulse : null,
child: const Text("Vibrate"),
),
const Spacer(),
ViewsSelector(index: index),
],
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(10),
child: Center(
child: ControllerWidget(model.selectedController),
),
),
),
],
);
}
35 changes: 35 additions & 0 deletions lib/src/pages/controllers/button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import "package:flutter/material.dart";

/// Represents a button that is pressed or not.
class ControllerButton extends StatelessWidget {
/// The radius with which to draw this button.
final double radius;

/// How thick the border should be.
final double borderWidth;

/// Whether the button is pressed or not.
final bool isPressed;

/// The color of the button.
final Color color;

/// Draws a small circle to represent a button that can be pressed.
const ControllerButton({
required this.isPressed,
required this.radius,
required this.borderWidth,
required this.color,
});

@override
Widget build(BuildContext context) => Container(
width: radius,
height: radius,
decoration: BoxDecoration(
color: isPressed ? color : null,
shape: BoxShape.circle,
border: Border.all(color: color, width: borderWidth),
),
);
}
73 changes: 73 additions & 0 deletions lib/src/pages/controllers/constants.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import "package:flutter/material.dart";

/// The color to fill in all gamepad buttons with.
const gamepadColor = Colors.blue;

/// The size of the controller image. Useful for overlaying elements on the picture.
const Size imageSize = Size(905, 568);

/// The radius for a button.
const double normalButtonRadius = 40;

/// The radius for a joystick.
const double normalJoystickRadius = 70;

/// The furthest a joystick can be off its center.
const double joystickMaxOffset = 40;

/// The width of the trigger bars.
const double normalTriggerWidth = 30;

/// The height of hte trigger bars.
const double normalTriggerHeight = 80;

/// The thickness of the trigger bars.
const double normalTriggerOutline = 10;

/// The position of the A button on the image.
const Offset buttonA = Offset(727, 230);

/// The position of the B button on the image.
const Offset buttonB = Offset(784, 173);

/// The position of the X button on the image.
const Offset buttonX = Offset(670, 173);

/// The position of the Y button on the image.
const Offset buttonY = Offset(727, 117);

/// The position of the left bumper/shoulder on the image.
const Offset leftBumper = Offset(186, 16);

/// The position of the right bumper/shoulder on the image.
const Offset rightBumper = Offset(726, 16);

/// The position of the left trigger on the image.
const Offset leftTrigger = Offset(40, 35);

/// The position of the right trigger on the image.
const Offset rightTrigger = Offset(872, 35);

/// The position of the select button on the image.
const Offset select = Offset(349, 112);

/// The position of the start button on the image.
const Offset start = Offset(558, 112);

/// The position of the up arrow button on the image.
const Offset dPadUp = Offset(180, 122);

/// The position of the down arrow button on the image.
const Offset dPadDown = Offset(180, 221);

/// The position of the left arrow button on the image.
const Offset dPadLeft = Offset(125, 175);

/// The position of the right arrow button on the image.
const Offset dPadRight = Offset(227, 175);

/// The position of the left joystick on the image.
const Offset leftStick = Offset(289, 295);

/// The position of the right joystic on the image.
const Offset rightStick = Offset(616, 295);
Loading

0 comments on commit 14066ef

Please sign in to comment.