diff --git a/Diagrams/.obsidian/app.json b/Diagrams/.obsidian/app.json
new file mode 100644
index 000000000000..9e26dfeeb6e6
--- /dev/null
+++ b/Diagrams/.obsidian/app.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/Diagrams/.obsidian/appearance.json b/Diagrams/.obsidian/appearance.json
new file mode 100644
index 000000000000..9e26dfeeb6e6
--- /dev/null
+++ b/Diagrams/.obsidian/appearance.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/Diagrams/.obsidian/core-plugins.json b/Diagrams/.obsidian/core-plugins.json
new file mode 100644
index 000000000000..639b90da7172
--- /dev/null
+++ b/Diagrams/.obsidian/core-plugins.json
@@ -0,0 +1,33 @@
+{
+ "file-explorer": true,
+ "global-search": true,
+ "switcher": true,
+ "graph": true,
+ "backlink": true,
+ "canvas": true,
+ "outgoing-link": true,
+ "tag-pane": true,
+ "footnotes": false,
+ "properties": true,
+ "page-preview": true,
+ "daily-notes": true,
+ "templates": true,
+ "note-composer": true,
+ "command-palette": true,
+ "slash-command": false,
+ "editor-status": true,
+ "bookmarks": true,
+ "markdown-importer": false,
+ "zk-prefixer": false,
+ "random-note": false,
+ "outline": true,
+ "word-count": true,
+ "slides": false,
+ "audio-recorder": false,
+ "workspaces": false,
+ "file-recovery": true,
+ "publish": false,
+ "sync": true,
+ "bases": true,
+ "webviewer": false
+}
\ No newline at end of file
diff --git a/Diagrams/.obsidian/workspace.json b/Diagrams/.obsidian/workspace.json
new file mode 100644
index 000000000000..b1ee9a62a17d
--- /dev/null
+++ b/Diagrams/.obsidian/workspace.json
@@ -0,0 +1,181 @@
+{
+ "main": {
+ "id": "d1bcf6a441f1ffa0",
+ "type": "split",
+ "children": [
+ {
+ "id": "93d2aa7072c111d2",
+ "type": "tabs",
+ "children": [
+ {
+ "id": "d3b4cee07f48b93b",
+ "type": "leaf",
+ "state": {
+ "type": "empty",
+ "state": {},
+ "icon": "lucide-file",
+ "title": "New tab"
+ }
+ }
+ ]
+ }
+ ],
+ "direction": "vertical"
+ },
+ "left": {
+ "id": "4ccc105dc4fded54",
+ "type": "split",
+ "children": [
+ {
+ "id": "675dba12eead9ff0",
+ "type": "tabs",
+ "children": [
+ {
+ "id": "a02f912852c9ffc1",
+ "type": "leaf",
+ "state": {
+ "type": "file-explorer",
+ "state": {
+ "sortOrder": "alphabetical",
+ "autoReveal": false
+ },
+ "icon": "lucide-folder-closed",
+ "title": "Files"
+ }
+ },
+ {
+ "id": "12123966e35c994f",
+ "type": "leaf",
+ "state": {
+ "type": "search",
+ "state": {
+ "query": "",
+ "matchingCase": false,
+ "explainSearch": false,
+ "collapseAll": false,
+ "extraContext": false,
+ "sortOrder": "alphabetical"
+ },
+ "icon": "lucide-search",
+ "title": "Search"
+ }
+ },
+ {
+ "id": "a919e2ce8b41c36e",
+ "type": "leaf",
+ "state": {
+ "type": "bookmarks",
+ "state": {},
+ "icon": "lucide-bookmark",
+ "title": "Bookmarks"
+ }
+ }
+ ]
+ }
+ ],
+ "direction": "horizontal",
+ "width": 300
+ },
+ "right": {
+ "id": "de8ad46ed00c92ec",
+ "type": "split",
+ "children": [
+ {
+ "id": "33d21acae245f288",
+ "type": "tabs",
+ "children": [
+ {
+ "id": "e24d0618d6282398",
+ "type": "leaf",
+ "state": {
+ "type": "backlink",
+ "state": {
+ "collapseAll": false,
+ "extraContext": false,
+ "sortOrder": "alphabetical",
+ "showSearch": false,
+ "searchQuery": "",
+ "backlinkCollapsed": false,
+ "unlinkedCollapsed": true
+ },
+ "icon": "links-coming-in",
+ "title": "Backlinks"
+ }
+ },
+ {
+ "id": "b1433e36081cfd0c",
+ "type": "leaf",
+ "state": {
+ "type": "outgoing-link",
+ "state": {
+ "linksCollapsed": false,
+ "unlinkedCollapsed": true
+ },
+ "icon": "links-going-out",
+ "title": "Outgoing links"
+ }
+ },
+ {
+ "id": "23f001e7431040ef",
+ "type": "leaf",
+ "state": {
+ "type": "tag",
+ "state": {
+ "sortOrder": "frequency",
+ "useHierarchy": true,
+ "showSearch": false,
+ "searchQuery": ""
+ },
+ "icon": "lucide-tags",
+ "title": "Tags"
+ }
+ },
+ {
+ "id": "9cc1240dcfab34d9",
+ "type": "leaf",
+ "state": {
+ "type": "all-properties",
+ "state": {
+ "sortOrder": "frequency",
+ "showSearch": false,
+ "searchQuery": ""
+ },
+ "icon": "lucide-archive",
+ "title": "All properties"
+ }
+ },
+ {
+ "id": "61df8a47ce9eeec7",
+ "type": "leaf",
+ "state": {
+ "type": "outline",
+ "state": {
+ "followCursor": false,
+ "showSearch": false,
+ "searchQuery": ""
+ },
+ "icon": "lucide-list",
+ "title": "Outline"
+ }
+ }
+ ]
+ }
+ ],
+ "direction": "horizontal",
+ "width": 300,
+ "collapsed": true
+ },
+ "left-ribbon": {
+ "hiddenItems": {
+ "switcher:Open quick switcher": false,
+ "graph:Open graph view": false,
+ "canvas:Create new canvas": false,
+ "daily-notes:Open today's daily note": false,
+ "templates:Insert template": false,
+ "command-palette:Open command palette": false,
+ "bases:Create new base": false
+ }
+ },
+ "active": "d3b4cee07f48b93b",
+ "lastOpenFiles": []
+}
\ No newline at end of file
diff --git a/Diagrams/File Structure.canvas b/Diagrams/File Structure.canvas
new file mode 100644
index 000000000000..815461432420
--- /dev/null
+++ b/Diagrams/File Structure.canvas
@@ -0,0 +1,652 @@
+{
+ "nodes":[
+ {
+ "id":"24bf9c010517c043",
+ "type":"group",
+ "styleAttributes":{
+ "border":null
+ },
+ "x":-480,
+ "y":-740,
+ "width":460,
+ "height":760,
+ "label":"Actuator Subsystems"
+ },
+ {"id":"10d2e8f294485406","type":"group","x":40,"y":-740,"width":360,"height":520,"label":"Generic Devices"},
+ {"id":"b257e56e060bcf72","type":"group","x":-1060,"y":-1080,"width":360,"height":300,"label":"Legend"},
+ {"id":"c387321b908f5578","type":"group","x":-480,"y":-1160,"width":240,"height":360,"label":"Drive Subsystem"},
+ {
+ "id":"6cb595b6641c5429",
+ "type":"text",
+ "text":"Turret",
+ "styleAttributes":{
+ "textAlign":"center",
+ "shape":null
+ },
+ "x":-440,
+ "y":-560,
+ "width":160,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"7c67cdb9e592c4a8",
+ "type":"text",
+ "text":"Actuators_",
+ "styleAttributes":{"textAlign":"center","shape":"database"},
+ "x":-440,
+ "y":-680,
+ "width":160,
+ "height":60
+ },
+ {
+ "id":"c60fab6b02e73c2d",
+ "type":"text",
+ "text":"Flywheel Motor",
+ "styleAttributes":{"textAlign":"center","shape":"pill"},
+ "x":-220,
+ "y":-560,
+ "width":160,
+ "height":60,
+ "color":"1"
+ },
+ {
+ "id":"4b4b925b55e20a4b",
+ "type":"text",
+ "text":"Rotation Motor",
+ "styleAttributes":{"textAlign":"center","shape":"pill"},
+ "x":-220,
+ "y":-480,
+ "width":160,
+ "height":60,
+ "color":"1"
+ },
+ {
+ "id":"b6ca95068fb87cd1",
+ "type":"text",
+ "text":"Specific\nDevices",
+ "styleAttributes":{"textAlign":"center","shape":"pill"},
+ "x":-860,
+ "y":-1060,
+ "width":140,
+ "height":60,
+ "color":"1"
+ },
+ {
+ "id":"1e2cad7776a30e09",
+ "type":"text",
+ "text":"Folders",
+ "styleAttributes":{"textAlign":"center","shape":"database"},
+ "x":-860,
+ "y":-960,
+ "width":140,
+ "height":60
+ },
+ {
+ "id":"fea165c26dfac7b4",
+ "type":"text",
+ "text":"Primary Classes",
+ "styleAttributes":{"textAlign":"center","shape":"predefined-process"},
+ "x":-1040,
+ "y":-1060,
+ "width":140,
+ "height":80,
+ "color":"6"
+ },
+ {
+ "id":"e39752447e25fe9b",
+ "type":"text",
+ "text":"Generic Device Classes",
+ "styleAttributes":{"textAlign":"center","shape":"circle"},
+ "x":-1040,
+ "y":-880,
+ "width":140,
+ "height":80,
+ "color":"3"
+ },
+ {
+ "id":"b3d29ccbc0207fba",
+ "type":"text",
+ "text":"Subsystem Classes",
+ "styleAttributes":{
+ "textAlign":"center",
+ "shape":null
+ },
+ "x":-1040,
+ "y":-960,
+ "width":140,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"36c91406159c570c",
+ "type":"text",
+ "text":"Lift",
+ "styleAttributes":{
+ "textAlign":"center",
+ "shape":null
+ },
+ "x":-440,
+ "y":-80,
+ "width":160,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"46567539d2a5035d",
+ "type":"text",
+ "text":"Lift Motor?",
+ "styleAttributes":{"textAlign":"center","shape":"pill"},
+ "x":-220,
+ "y":-80,
+ "width":160,
+ "height":60,
+ "color":"1"
+ },
+ {
+ "id":"38a0d38b66a11a14",
+ "type":"text",
+ "text":"Intake Motor",
+ "styleAttributes":{"textAlign":"center","shape":"pill"},
+ "x":-220,
+ "y":-320,
+ "width":160,
+ "height":60,
+ "color":"1"
+ },
+ {
+ "id":"4d43db858659fbed",
+ "type":"text",
+ "text":"Belt Servo",
+ "styleAttributes":{"textAlign":"center","shape":"pill"},
+ "x":-220,
+ "y":-240,
+ "width":160,
+ "height":60,
+ "color":"1"
+ },
+ {
+ "id":"29fb84e8134463b3",
+ "type":"text",
+ "text":"Transfer Servo",
+ "styleAttributes":{"textAlign":"center","shape":"pill"},
+ "x":-220,
+ "y":-160,
+ "width":160,
+ "height":60,
+ "color":"1"
+ },
+ {
+ "id":"d8d4dacf5920c2cb",
+ "type":"text",
+ "text":"Hood Servo",
+ "styleAttributes":{"textAlign":"center","shape":"pill"},
+ "x":-220,
+ "y":-400,
+ "width":160,
+ "height":60,
+ "color":"1"
+ },
+ {
+ "id":"3fe96a913c4c7045",
+ "type":"text",
+ "text":"Hardware_",
+ "styleAttributes":{"textAlign":"center","shape":"database"},
+ "x":-1020,
+ "y":-680,
+ "width":140,
+ "height":60
+ },
+ {
+ "id":"b0917a192e867cbe",
+ "type":"text",
+ "text":"Teleop_",
+ "styleAttributes":{"textAlign":"center","shape":"database"},
+ "x":-1020,
+ "y":-540,
+ "width":140,
+ "height":60
+ },
+ {
+ "id":"10a4fa2477f2c9da",
+ "type":"text",
+ "text":"Main_",
+ "styleAttributes":{"textAlign":"center","shape":"database"},
+ "x":-1220,
+ "y":-540,
+ "width":140,
+ "height":60
+ },
+ {
+ "id":"781182d601f21fe3",
+ "type":"text",
+ "text":"Intake",
+ "styleAttributes":{
+ "textAlign":"center",
+ "shape":null
+ },
+ "x":-440,
+ "y":-320,
+ "width":160,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"b681d02738b88d07",
+ "type":"text",
+ "text":"TeleOp Control",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-440,
+ "y":-980,
+ "width":160,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"3cd44b1464241515",
+ "type":"text",
+ "text":"Processor Classes",
+ "styleAttributes":{"textAlign":"center","shape":"parallelogram"},
+ "x":-860,
+ "y":-860,
+ "width":140,
+ "height":60,
+ "color":"#6efe6c"
+ },
+ {
+ "id":"d728e0a74da6c0fd",
+ "type":"text",
+ "text":"Auto_",
+ "styleAttributes":{"textAlign":"center","shape":"database"},
+ "x":-1020,
+ "y":-400,
+ "width":140,
+ "height":60
+ },
+ {
+ "id":"c950be02cfc42181",
+ "type":"text",
+ "text":"Drivetrain_",
+ "styleAttributes":{"textAlign":"center","shape":"database"},
+ "x":-440,
+ "y":-1100,
+ "width":160,
+ "height":60
+ },
+ {
+ "id":"20c263df8c9982a4",
+ "type":"text",
+ "text":"Auto Pathing",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-440,
+ "y":-900,
+ "width":160,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"f8f6dfb640cbfd1d",
+ "type":"text",
+ "text":"Devices",
+ "styleAttributes":{
+ "textAlign":"center",
+ "shape":"circle",
+ "border":null
+ },
+ "x":120,
+ "y":-700,
+ "width":200,
+ "height":100,
+ "color":"3"
+ },
+ {
+ "id":"ce3a4399dc294b02",
+ "type":"text",
+ "text":"Servo",
+ "styleAttributes":{"textAlign":"center","shape":"circle"},
+ "x":160,
+ "y":-400,
+ "width":120,
+ "height":60,
+ "color":"3"
+ },
+ {
+ "id":"b31e377ce216f382",
+ "type":"text",
+ "text":"DcMotorEx",
+ "styleAttributes":{"textAlign":"center","shape":"circle"},
+ "x":80,
+ "y":-480,
+ "width":120,
+ "height":60,
+ "color":"3"
+ },
+ {
+ "id":"64070cd56adeb48a",
+ "type":"text",
+ "text":"CRServo",
+ "styleAttributes":{"textAlign":"center","shape":"circle"},
+ "x":240,
+ "y":-320,
+ "width":120,
+ "height":60,
+ "color":"3"
+ },
+ {
+ "id":"cb4e9f8e5320c422",
+ "type":"text",
+ "text":"Robot (LOAD_Hardware_Class)",
+ "styleAttributes":{"textAlign":"center","shape":"predefined-process"},
+ "x":-820,
+ "y":-620,
+ "width":260,
+ "height":60,
+ "color":"6"
+ }
+ ],
+ "edges":[
+ {
+ "id":"a754ca4983fc848a",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"781182d601f21fe3",
+ "fromSide":"right",
+ "toNode":"38a0d38b66a11a14",
+ "toSide":"left"
+ },
+ {
+ "id":"1ff69a492a801c93",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"781182d601f21fe3",
+ "fromSide":"right",
+ "toNode":"4d43db858659fbed",
+ "toSide":"left"
+ },
+ {
+ "id":"63cb6bd74dfe514d",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"781182d601f21fe3",
+ "fromSide":"right",
+ "toNode":"29fb84e8134463b3",
+ "toSide":"left"
+ },
+ {
+ "id":"f39b6af2b8e48183",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"6cb595b6641c5429",
+ "fromSide":"right",
+ "toNode":"c60fab6b02e73c2d",
+ "toSide":"left"
+ },
+ {
+ "id":"67ba26aea9f89a01",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"6cb595b6641c5429",
+ "fromSide":"right",
+ "toNode":"4b4b925b55e20a4b",
+ "toSide":"left"
+ },
+ {
+ "id":"e4ac46a6a34dfc96",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"6cb595b6641c5429",
+ "fromSide":"right",
+ "toNode":"d8d4dacf5920c2cb",
+ "toSide":"left"
+ },
+ {
+ "id":"1a3c223f60e7960e",
+ "styleAttributes":{},
+ "toFloating":false,
+ "fromNode":"36c91406159c570c",
+ "fromSide":"right",
+ "toNode":"46567539d2a5035d",
+ "toSide":"left"
+ },
+ {
+ "id":"75fb7926f00bf1f2",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"b31e377ce216f382",
+ "fromSide":"left",
+ "toNode":"38a0d38b66a11a14",
+ "toSide":"right"
+ },
+ {
+ "id":"e502478432f11ab2",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"b31e377ce216f382",
+ "fromSide":"left",
+ "toNode":"c60fab6b02e73c2d",
+ "toSide":"right"
+ },
+ {
+ "id":"3d6234ec9ba8b80a",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"b31e377ce216f382",
+ "fromSide":"left",
+ "toNode":"4b4b925b55e20a4b",
+ "toSide":"right"
+ },
+ {
+ "id":"ecf36fe8628d23d4",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"b31e377ce216f382",
+ "fromSide":"left",
+ "toNode":"46567539d2a5035d",
+ "toSide":"right"
+ },
+ {
+ "id":"e520deaeb33f18ea",
+ "styleAttributes":{"pathfindingMethod":"square","path":"dotted"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"10a4fa2477f2c9da",
+ "fromSide":"right",
+ "toNode":"3fe96a913c4c7045",
+ "toSide":"left"
+ },
+ {
+ "id":"a490a71e7c0889c4",
+ "styleAttributes":{"pathfindingMethod":"square","path":"dotted"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"10a4fa2477f2c9da",
+ "fromSide":"right",
+ "toNode":"b0917a192e867cbe",
+ "toSide":"left"
+ },
+ {
+ "id":"fee5e7b645c72d49",
+ "styleAttributes":{"pathfindingMethod":"square","path":"dotted"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"10a4fa2477f2c9da",
+ "fromSide":"right",
+ "toNode":"d728e0a74da6c0fd",
+ "toSide":"left"
+ },
+ {
+ "id":"c27b4cd5b53a39a1",
+ "styleAttributes":{"pathfindingMethod":"square","path":"dotted"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"3fe96a913c4c7045",
+ "fromSide":"right",
+ "toNode":"cb4e9f8e5320c422",
+ "toSide":"left"
+ },
+ {
+ "id":"cb54500a03c6dae8",
+ "styleAttributes":{"pathfindingMethod":"square","path":"dotted"},
+ "toFloating":false,
+ "fromNode":"3fe96a913c4c7045",
+ "fromSide":"right",
+ "toNode":"7c67cdb9e592c4a8",
+ "toSide":"left"
+ },
+ {
+ "id":"5a67ba206c21c5d5",
+ "styleAttributes":{"pathfindingMethod":"square","path":"dotted"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"3fe96a913c4c7045",
+ "fromSide":"right",
+ "toNode":"c950be02cfc42181",
+ "toSide":"left"
+ },
+ {
+ "id":"730fdab1703e2384",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"cb4e9f8e5320c422",
+ "fromSide":"right",
+ "toNode":"36c91406159c570c",
+ "toSide":"left"
+ },
+ {
+ "id":"227ee64c216dd8a2",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"cb4e9f8e5320c422",
+ "fromSide":"right",
+ "toNode":"6cb595b6641c5429",
+ "toSide":"left"
+ },
+ {
+ "id":"da5b6b4b304e2a2a",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"cb4e9f8e5320c422",
+ "fromSide":"right",
+ "toNode":"781182d601f21fe3",
+ "toSide":"left"
+ },
+ {
+ "id":"600959ea0be584d1",
+ "styleAttributes":{"pathfindingMethod":"square","path":"short-dashed"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"ce3a4399dc294b02",
+ "fromSide":"left",
+ "toNode":"29fb84e8134463b3",
+ "toSide":"right"
+ },
+ {
+ "id":"59e449d6190f7c62",
+ "styleAttributes":{"pathfindingMethod":"square","path":"short-dashed"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"ce3a4399dc294b02",
+ "fromSide":"left",
+ "toNode":"d8d4dacf5920c2cb",
+ "toSide":"right"
+ },
+ {
+ "id":"b4c105fc0c9f9a0b",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"cb4e9f8e5320c422",
+ "fromSide":"right",
+ "toNode":"20c263df8c9982a4",
+ "toSide":"left"
+ },
+ {
+ "id":"6c06e6161a19b57b",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"cb4e9f8e5320c422",
+ "fromSide":"right",
+ "toNode":"b681d02738b88d07",
+ "toSide":"left"
+ },
+ {
+ "id":"c3e114e590ec207f",
+ "styleAttributes":{
+ "path":"dotted",
+ "arrow":null
+ },
+ "toFloating":false,
+ "fromNode":"7c67cdb9e592c4a8",
+ "fromSide":"bottom",
+ "toNode":"36c91406159c570c",
+ "toSide":"top"
+ },
+ {
+ "id":"b0839d6e6e68d597",
+ "styleAttributes":{},
+ "toFloating":false,
+ "fromNode":"c950be02cfc42181",
+ "fromSide":"bottom",
+ "toNode":"20c263df8c9982a4",
+ "toSide":"top"
+ },
+ {
+ "id":"3c47645c9d187d74",
+ "styleAttributes":{"path":"long-dashed","pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"64070cd56adeb48a",
+ "fromSide":"left",
+ "toNode":"4d43db858659fbed",
+ "toSide":"right"
+ },
+ {
+ "id":"7e25a3f58e5e8145",
+ "styleAttributes":{"path":"dotted"},
+ "toFloating":false,
+ "fromNode":"7c67cdb9e592c4a8",
+ "fromSide":"right",
+ "toNode":"f8f6dfb640cbfd1d",
+ "toSide":"left"
+ },
+ {
+ "id":"6befddbfcd1ec457",
+ "styleAttributes":{"path":"dotted"},
+ "toFloating":false,
+ "fromNode":"f8f6dfb640cbfd1d",
+ "fromSide":"bottom",
+ "toNode":"ce3a4399dc294b02",
+ "toSide":"top"
+ },
+ {
+ "id":"bb0e476818e13bff",
+ "styleAttributes":{"pathfindingMethod":"square","path":"dotted"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"f8f6dfb640cbfd1d",
+ "fromSide":"bottom",
+ "toNode":"b31e377ce216f382",
+ "toSide":"top"
+ },
+ {
+ "id":"450188924918c729",
+ "styleAttributes":{"pathfindingMethod":"square","path":"dotted"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"f8f6dfb640cbfd1d",
+ "fromSide":"bottom",
+ "toNode":"64070cd56adeb48a",
+ "toSide":"top"
+ }
+ ],
+ "metadata":{
+ "version":"1.0-1.0",
+ "frontmatter":{},
+ "startNode":"0b1473b84ec5c5d3"
+ }
+}
\ No newline at end of file
diff --git a/Diagrams/File_Structure.png b/Diagrams/File_Structure.png
new file mode 100644
index 000000000000..166df00c2742
Binary files /dev/null and b/Diagrams/File_Structure.png differ
diff --git a/Diagrams/Robot_Controls.png b/Diagrams/Robot_Controls.png
new file mode 100644
index 000000000000..cedc746d8180
Binary files /dev/null and b/Diagrams/Robot_Controls.png differ
diff --git a/Diagrams/Robot_Wiring.png b/Diagrams/Robot_Wiring.png
new file mode 100644
index 000000000000..8b4808bb1128
Binary files /dev/null and b/Diagrams/Robot_Wiring.png differ
diff --git a/Diagrams/Wiring.canvas b/Diagrams/Wiring.canvas
new file mode 100644
index 000000000000..ea4cd43f646f
--- /dev/null
+++ b/Diagrams/Wiring.canvas
@@ -0,0 +1,485 @@
+{
+ "nodes":[
+ {"id":"85dd3f12ac72d900","type":"group","x":-360,"y":-440,"width":380,"height":260,"label":"Legend"},
+ {
+ "id":"c2eee2656a3ddfa1",
+ "type":"text",
+ "text":"Pinpoint Computer\nI2C Port 1",
+ "styleAttributes":{"textAlign":"center"},
+ "x":80,
+ "y":-260,
+ "width":160,
+ "height":60,
+ "color":"6"
+ },
+ {
+ "id":"e20a52ef21d57dcf",
+ "type":"text",
+ "text":"LED Controller\nI2C Port 2",
+ "styleAttributes":{"textAlign":"center"},
+ "x":80,
+ "y":-340,
+ "width":160,
+ "height":60,
+ "color":"6"
+ },
+ {
+ "id":"f9a634b23b5064c5",
+ "type":"text",
+ "text":"Servos",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-340,
+ "y":-340,
+ "width":160,
+ "height":60,
+ "color":"4"
+ },
+ {
+ "id":"48faa728dd92705d",
+ "type":"text",
+ "text":"Motors",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-160,
+ "y":-340,
+ "width":160,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"26e535ad3414017c",
+ "type":"text",
+ "text":"Control System",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-340,
+ "y":-260,
+ "width":160,
+ "height":60,
+ "color":"2"
+ },
+ {
+ "id":"2cad408dc2aae848",
+ "type":"text",
+ "text":"I2C Devices",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-160,
+ "y":-260,
+ "width":160,
+ "height":60,
+ "color":"6"
+ },
+ {
+ "id":"a0abe0901b65ac56",
+ "type":"text",
+ "text":"Upper Color Sensors\nI2C Ports 0-1",
+ "styleAttributes":{"textAlign":"center"},
+ "x":300,
+ "y":-340,
+ "width":160,
+ "height":60,
+ "color":"6"
+ },
+ {
+ "id":"e3fdefe17fe2310d",
+ "type":"text",
+ "text":"Analog Devices",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-160,
+ "y":-420,
+ "width":160,
+ "height":60,
+ "color":"3"
+ },
+ {
+ "id":"7a1b2108fa3bc30e",
+ "type":"text",
+ "text":"Hall Effect Sensor\nPort 0",
+ "styleAttributes":{"textAlign":"center"},
+ "x":80,
+ "y":-420,
+ "width":160,
+ "height":60,
+ "color":"1"
+ },
+ {
+ "id":"81a20594db9883e0",
+ "type":"text",
+ "text":"Control Hub",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-80,
+ "y":-160,
+ "width":260,
+ "height":60,
+ "color":"2"
+ },
+ {
+ "id":"d9c005396002882a",
+ "type":"text",
+ "text":"Expansion Hub",
+ "styleAttributes":{"textAlign":"center"},
+ "x":360,
+ "y":-160,
+ "width":260,
+ "height":60,
+ "color":"2"
+ },
+ {
+ "id":"f667221e45b8fc86",
+ "type":"text",
+ "text":"Intake Motor\nPort 0 - No Encoder",
+ "styleAttributes":{"textAlign":"center"},
+ "x":520,
+ "y":-60,
+ "width":260,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"0075a4079a1a472d",
+ "type":"text",
+ "text":"Front Right Drive Motor\nPort 1 - No Encoder",
+ "styleAttributes":{"textAlign":"center"},
+ "x":520,
+ "y":20,
+ "width":260,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"5587942ea5903dbe",
+ "type":"text",
+ "text":"Back Right Drive Motor\nPort 2 - No Encoder",
+ "styleAttributes":{"textAlign":"center"},
+ "x":520,
+ "y":100,
+ "width":260,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"c6d790ec667c829f",
+ "type":"text",
+ "text":"Turret Rotation Motor\nPort 3 - With Encoder",
+ "styleAttributes":{"textAlign":"center"},
+ "x":520,
+ "y":180,
+ "width":260,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"d97803361edf47ba",
+ "type":"text",
+ "text":"Lower Color Sensors\nI2C Ports 2-3",
+ "styleAttributes":{"textAlign":"center"},
+ "x":300,
+ "y":-260,
+ "width":160,
+ "height":60,
+ "color":"6"
+ },
+ {
+ "id":"86792695c78b2d59",
+ "type":"text",
+ "text":"Servo Power Injector",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-440,
+ "y":-160,
+ "width":180,
+ "height":60,
+ "color":"2"
+ },
+ {
+ "id":"88b48890e8f34fc1",
+ "type":"text",
+ "text":"The Servo Power Injector ports correspond to the Control Hub Ports.\nChub P0 -> SPI P1, Chub P1 -> SPI P2, etc. The given ports are for the SPI, not the control hub.",
+ "styleAttributes":{
+ "textAlign":"center",
+ "shape":null,
+ "border":null
+ },
+ "x":-540,
+ "y":-40,
+ "width":160,
+ "height":260
+ },
+ {
+ "id":"bfb02b2a92b84ad2",
+ "type":"text",
+ "text":"Belt Servo - Port 1",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-300,
+ "y":-60,
+ "width":200,
+ "height":60,
+ "color":"4"
+ },
+ {
+ "id":"440a120e5375c485",
+ "type":"text",
+ "text":"Front Left Drive Motor\nPort 0 - No Encoder",
+ "styleAttributes":{"textAlign":"center"},
+ "x":80,
+ "y":-60,
+ "width":260,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"76edd4abcf05a2a3",
+ "type":"text",
+ "text":"Back Left Drive Motor\nPort 1 - No Encoder",
+ "styleAttributes":{"textAlign":"center"},
+ "x":80,
+ "y":20,
+ "width":260,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"336d50b5d4cb968f",
+ "type":"text",
+ "text":"Flywheel Motor 1\nPort 2 - With Encoder",
+ "styleAttributes":{"textAlign":"center"},
+ "x":80,
+ "y":100,
+ "width":260,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"f8b39583a022e492",
+ "type":"text",
+ "text":"Flywheel Motor 2\nPort 3 - No Encoder",
+ "styleAttributes":{"textAlign":"center"},
+ "x":80,
+ "y":180,
+ "width":260,
+ "height":60,
+ "color":"5"
+ },
+ {
+ "id":"41c5560b3c8edc13",
+ "type":"text",
+ "text":"Hood Servo - Port 2",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-300,
+ "y":20,
+ "width":200,
+ "height":60,
+ "color":"4"
+ },
+ {
+ "id":"b2918b70ae0b4303",
+ "type":"text",
+ "text":"Gate Servo - Port 3",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-300,
+ "y":100,
+ "width":200,
+ "height":60,
+ "color":"4"
+ },
+ {
+ "id":"8c94376760075734",
+ "type":"text",
+ "text":"Transfer Servo - Port 4",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-300,
+ "y":180,
+ "width":200,
+ "height":60,
+ "color":"4"
+ },
+ {
+ "id":"b7514b6ae54e27cf",
+ "type":"text",
+ "text":"Digital Devices",
+ "styleAttributes":{"textAlign":"center"},
+ "x":-340,
+ "y":-420,
+ "width":160,
+ "height":60,
+ "color":"1"
+ }
+ ],
+ "edges":[
+ {
+ "id":"b19d432c983955cf",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"81a20594db9883e0",
+ "fromSide":"bottom",
+ "toNode":"440a120e5375c485",
+ "toSide":"left"
+ },
+ {
+ "id":"d76f3cbbdeee44de",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"81a20594db9883e0",
+ "fromSide":"bottom",
+ "toNode":"76edd4abcf05a2a3",
+ "toSide":"left"
+ },
+ {
+ "id":"f3883787e1cf36ff",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"81a20594db9883e0",
+ "fromSide":"bottom",
+ "toNode":"336d50b5d4cb968f",
+ "toSide":"left"
+ },
+ {
+ "id":"5b0001153bf55d91",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"81a20594db9883e0",
+ "fromSide":"bottom",
+ "toNode":"f8b39583a022e492",
+ "toSide":"left"
+ },
+ {
+ "id":"2ef75997821cccc9",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"d9c005396002882a",
+ "fromSide":"bottom",
+ "toNode":"f667221e45b8fc86",
+ "toSide":"left"
+ },
+ {
+ "id":"6eca4d1d11415dae",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"d9c005396002882a",
+ "fromSide":"bottom",
+ "toNode":"0075a4079a1a472d",
+ "toSide":"left"
+ },
+ {
+ "id":"d70db43e95185aa4",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"d9c005396002882a",
+ "fromSide":"bottom",
+ "toNode":"5587942ea5903dbe",
+ "toSide":"left"
+ },
+ {
+ "id":"94d25591b9c935b0",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"d9c005396002882a",
+ "fromSide":"bottom",
+ "toNode":"c6d790ec667c829f",
+ "toSide":"left"
+ },
+ {
+ "id":"4a02d336a8851951",
+ "styleAttributes":{},
+ "toFloating":false,
+ "fromNode":"81a20594db9883e0",
+ "fromSide":"right",
+ "toNode":"d9c005396002882a",
+ "toSide":"left"
+ },
+ {
+ "id":"50becea15ce0c423",
+ "styleAttributes":{},
+ "toFloating":false,
+ "fromFloating":false,
+ "fromNode":"81a20594db9883e0",
+ "fromSide":"left",
+ "toNode":"86792695c78b2d59",
+ "toSide":"right"
+ },
+ {
+ "id":"2994660c601b60c7",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"86792695c78b2d59",
+ "fromSide":"bottom",
+ "toNode":"bfb02b2a92b84ad2",
+ "toSide":"left"
+ },
+ {
+ "id":"489912cd2dc5605d",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"86792695c78b2d59",
+ "fromSide":"bottom",
+ "toNode":"41c5560b3c8edc13",
+ "toSide":"left"
+ },
+ {
+ "id":"6e6343e8a6c4769c",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"86792695c78b2d59",
+ "fromSide":"bottom",
+ "toNode":"b2918b70ae0b4303",
+ "toSide":"left"
+ },
+ {
+ "id":"18ec8f8b3be09fca",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"86792695c78b2d59",
+ "fromSide":"bottom",
+ "toNode":"8c94376760075734",
+ "toSide":"left"
+ },
+ {
+ "id":"811cf14f46b67298",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"81a20594db9883e0",
+ "fromSide":"top",
+ "toNode":"c2eee2656a3ddfa1",
+ "toSide":"left"
+ },
+ {
+ "id":"c6a6b035bf824dfe",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"81a20594db9883e0",
+ "fromSide":"top",
+ "toNode":"e20a52ef21d57dcf",
+ "toSide":"left"
+ },
+ {
+ "id":"5c79a21468a43736",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"d9c005396002882a",
+ "fromSide":"top",
+ "toNode":"a0abe0901b65ac56",
+ "toSide":"right"
+ },
+ {
+ "id":"a6ee77457b2001f7",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"d9c005396002882a",
+ "fromSide":"top",
+ "toNode":"d97803361edf47ba",
+ "toSide":"right"
+ },
+ {
+ "id":"21b147d9374eee41",
+ "styleAttributes":{"pathfindingMethod":"square"},
+ "toFloating":false,
+ "fromNode":"81a20594db9883e0",
+ "fromSide":"top",
+ "toNode":"7a1b2108fa3bc30e",
+ "toSide":"left"
+ }
+ ],
+ "metadata":{
+ "version":"1.0-1.0",
+ "frontmatter":{}
+ }
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 522fb8048e75..4a2abcefd3a3 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,19 @@
+# LOAD Robotics DECODE Robot Code
+This repository contains LOAD Robotics' robot code for the 2025-2026 FTC season DECODE.
+
## NOTICE
This repository contains the public FTC SDK for the DECODE (2025-2026) competition season.
+
+## LOAD Charts & Diagrams
+### Our Filestructure
+
+### Robot Control Scheme (Pre December 6th)
+
+### Robot Wiring Diagram
+
+
## Welcome!
This GitHub repository contains the source code that is used to build an Android app to control a *FIRST* Tech Challenge competition robot. To use this SDK, download/clone the entire project to your local computer.
diff --git a/TeamCode/build.gradle b/TeamCode/build.gradle
index 878287a7c712..ae2a311874eb 100644
--- a/TeamCode/build.gradle
+++ b/TeamCode/build.gradle
@@ -25,4 +25,9 @@ android {
dependencies {
implementation project(':FtcRobotController')
+ implementation 'com.skeletonarmyftc.marrow:core:1.0.0'
+ implementation 'dev.nextftc:ftc:1.0.1'
+ implementation 'dev.nextftc.extensions:pedro:1.0.0'
+ implementation "com.bylazar:graph:1.0.4"
+ //implementation 'com.github.goBILDA-Official:FtcRobotController-Add-Prism:goBILDA-Prism-Driver' // For Light Strips
}
diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Auto_/Auto_Dec6th.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Auto_/Auto_Dec6th.java
new file mode 100644
index 000000000000..fc0786fae05d
--- /dev/null
+++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Auto_/Auto_Dec6th.java
@@ -0,0 +1,97 @@
+package org.firstinspires.ftc.teamcode.LOADCode.Main_.Auto_; // make sure this aligns with class location
+
+import static org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.LoadHardwareClass.selectedAlliance;
+
+import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
+import com.qualcomm.robotcore.eventloop.opmode.Disabled;
+import com.qualcomm.robotcore.eventloop.opmode.OpMode;
+import com.qualcomm.robotcore.hardware.DcMotorEx;
+import com.qualcomm.robotcore.hardware.DcMotorSimple;
+import com.skeletonarmy.marrow.prompts.OptionPrompt;
+import com.skeletonarmy.marrow.prompts.Prompter;
+
+import org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.LoadHardwareClass;
+@Disabled
+@Autonomous(name = "Auto_Dec6th", group = "TestAuto", preselectTeleOp="Teleop_Main_")
+public class Auto_Dec6th extends OpMode {
+
+ DcMotorEx FL;
+ DcMotorEx FR;
+ DcMotorEx BL;
+ DcMotorEx BR;
+
+ // Create the prompter object for selecting Alliance and Auto
+ Prompter prompter = null;
+
+ /**
+ * Copied over from LinearOpMode.
+ * @param milliseconds The number of milliseconds to sleep.
+ */
+ public final void sleep(long milliseconds) {
+ try {
+ Thread.sleep(milliseconds);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ /** This method is called once at the init of the OpMode. **/
+ @Override
+ public void init() {
+ FL = hardwareMap.get(DcMotorEx.class, "FL");
+ FR = hardwareMap.get(DcMotorEx.class, "FR");
+ BL = hardwareMap.get(DcMotorEx.class, "BL");
+ BR = hardwareMap.get(DcMotorEx.class, "BR");
+
+ FL.setDirection(DcMotorSimple.Direction.REVERSE);
+ BL.setDirection(DcMotorSimple.Direction.REVERSE);
+
+ prompter = new Prompter(this);
+ prompter.prompt("alliance",
+ new OptionPrompt<>("Select Alliance",
+ LoadHardwareClass.Alliance.RED,
+ LoadHardwareClass.Alliance.BLUE
+ ));
+ prompter.onComplete(() -> {
+ selectedAlliance = prompter.get("alliance");
+ telemetry.addData("Selection", "Complete");
+ telemetry.addData("Alliance", selectedAlliance);
+ telemetry.update();
+ }
+ );
+ }
+
+ /** This method is called continuously after Init while waiting for "play". **/
+ @Override
+ public void init_loop() {
+ prompter.run();
+ }
+
+ /** This method is called once at the start of the OpMode.
+ * It runs all the setup actions, including building paths and starting the path system **/
+ @Override
+ public void start() {
+ sleep(25000);
+ FL.setPower(0.3);
+ FR.setPower(0.3);
+ BL.setPower(0.3);
+ BR.setPower(0.3);
+ sleep(1700);
+ FL.setPower(0);
+ FR.setPower(0);
+ BL.setPower(0);
+ BR.setPower(0);
+ }
+
+ /** This is the main loop of the OpMode, it will run repeatedly after clicking "Play". **/
+ @Override
+ public void loop() {
+
+ }
+
+ /** We do not use this because everything should automatically disable **/
+ @Override
+ public void stop() {
+
+ }
+}
\ No newline at end of file
diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Auto_/Auto_Main_.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Auto_/Auto_Main_.java
new file mode 100644
index 000000000000..978d0c6fa1e8
--- /dev/null
+++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Auto_/Auto_Main_.java
@@ -0,0 +1,340 @@
+package org.firstinspires.ftc.teamcode.LOADCode.Main_.Auto_;
+
+import static org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.LoadHardwareClass.selectedAlliance;
+import static dev.nextftc.extensions.pedro.PedroComponent.follower;
+
+import androidx.annotation.NonNull;
+
+import com.pedropathing.geometry.Pose;
+import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
+import com.skeletonarmy.marrow.prompts.OptionPrompt;
+import com.skeletonarmy.marrow.prompts.Prompter;
+
+import org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.Actuators_.Intake;
+import org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.Actuators_.Turret;
+import org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.Commands;
+import org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.Drivetrain_.MecanumDrivetrainClass;
+import org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.Drivetrain_.Pedro_Paths;
+import org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.LoadHardwareClass;
+import org.firstinspires.ftc.teamcode.pedroPathing.Constants;
+
+import dev.nextftc.core.commands.delays.Delay;
+import dev.nextftc.core.commands.groups.SequentialGroup;
+import dev.nextftc.core.commands.utility.InstantCommand;
+import dev.nextftc.extensions.pedro.PedroComponent;
+import dev.nextftc.ftc.NextFTCOpMode;
+
+@Autonomous(name = "Auto_Main_", group = "Main", preselectTeleOp="Teleop_Main_")
+public class Auto_Main_ extends NextFTCOpMode {
+
+ // Variable to store the selected auto program
+ Auto selectedAuto = null;
+ // Create the prompter object for selecting Alliance and Auto
+ Prompter prompter = null;
+ // Create a new instance of our Robot class
+ LoadHardwareClass Robot = new LoadHardwareClass(this);
+ // Create a Paths object for accessing modular auto paths
+ Pedro_Paths paths = new Pedro_Paths();
+ // Create a Commands object for auto creation
+ Commands Commands = new Commands(Robot);
+
+ // Auto parameter variables
+ private boolean turretOn = true;
+
+ @SuppressWarnings("unused")
+ public Auto_Main_() {
+ addComponents(
+ new PedroComponent(Constants::createFollower)
+ );
+ }
+
+ @Override
+ public void onInit() {
+ prompter = new Prompter(this);
+ prompter.prompt("alliance",
+ new OptionPrompt<>("Select Alliance",
+ LoadHardwareClass.Alliance.RED,
+ LoadHardwareClass.Alliance.BLUE
+ ));
+ prompter.prompt("auto",
+ new OptionPrompt<>("Select Auto",
+ new Near_12Ball(),
+ new Near_9Ball(),
+ new Leave_Far_Generic()
+ //new test_Auto(),
+ //new Complex_Test_Auto()
+ ));
+ prompter.onComplete(() -> {
+ selectedAlliance = prompter.get("alliance");
+ selectedAuto = prompter.get("auto");
+ telemetry.addData("Selection", "Complete");
+ telemetry.addData("Alliance", selectedAlliance.toString());
+ telemetry.addData("Auto", selectedAuto);
+ telemetry.update();
+ // Build paths
+ paths.buildPaths(selectedAlliance, follower());
+ // Initialize all hardware of the robot
+ Robot.init(paths.autoMirror(selectedAuto.getStartPose(), selectedAlliance), follower());
+ while (opModeInInit() && Robot.turret.zeroTurret()){
+ sleep(0);
+ }
+ });
+ }
+
+ @Override
+ public void onWaitForStart() {
+ prompter.run();
+ }
+
+ @Override
+ public void onStartButtonPressed() {
+ // Schedule the proper auto
+ selectedAuto.runAuto();
+ turretOn = selectedAuto.getTurretEnabled();
+
+ // Indicate that initialization is done
+ telemetry.addLine("Initialized");
+ telemetry.update();
+ }
+
+ @Override
+ public void onUpdate() {
+ telemetry.addData("Running Auto", selectedAuto.toString());
+ telemetry.addLine();
+ if (turretOn){
+ telemetry.addData("Aimbot Target", selectedAlliance);
+ }else{
+ telemetry.addLine("Aimbot Off");
+ }
+ Robot.turret.updateAimbot(turretOn, true, 0);
+ Robot.turret.updateFlywheel();
+
+ MecanumDrivetrainClass.robotPose = Robot.drivetrain.follower.getPose();
+
+ telemetry.addLine();
+ telemetry.addData("FarStart", paths.farStart);
+ telemetry.addData("FarShoot", paths.noTurretFarShoot);
+ telemetry.update();
+ }
+
+ @Override
+ public void onStop(){
+ Robot.drivetrain.follower.holdPoint(Robot.drivetrain.follower.getPose());
+ MecanumDrivetrainClass.robotPose = Robot.drivetrain.follower.getPose();
+ }
+
+ /**
+ * This class serves as a template for all auto programs.
+ * The methods runAuto() and ToString() must be overridden for each auto.
+ */
+ abstract static class Auto{
+// /**
+// * This constructor must be called from the child class using super()
+// * @param startingPose Indicates the starting pose of the robot
+// * @param runTurret Indicates whether to run the turret auto aim functions
+// */
+// Auto(Pose startingPose, Boolean runTurret){
+// turretOn = runTurret;
+// startPose = startingPose;
+// }
+// Auto(Pose startingPose){
+// turretOn = true;
+// startPose = startingPose;
+// }
+
+ abstract Pose getStartPose();
+ abstract boolean getTurretEnabled();
+
+ /** Override this to schedule the auto command*/
+ abstract void runAuto();
+ /** Override this to rename the auto*/
+ @NonNull
+ @Override
+ public abstract String toString();
+ }
+
+ private class Leave_Far_Generic extends Auto{
+ public Pose startPose = paths.farStart;
+ public boolean turretEnabled = false;
+
+ @Override
+ public Pose getStartPose(){
+ return startPose;
+ }
+ @Override
+ public boolean getTurretEnabled(){
+ return turretEnabled;
+ }
+
+ @Override
+ public void runAuto(){
+ new SequentialGroup(
+ Commands.runPath(paths.farStart_to_NoTurret_FarShoot, true, 1),
+ Commands.shootBalls(),
+ Commands.setIntakeMode(Intake.intakeMode.INTAKING),
+ Commands.runPath(paths.farShoot_noTurret_to_farPreload, true, 1),
+ Commands.setIntakeMode(Intake.intakeMode.OFF)
+ ).schedule();
+ }
+
+ @NonNull
+ @Override
+ public String toString(){return "Far Zone No Turret Generic";}
+ }
+
+ private class Near_9Ball extends Auto{
+ public Pose startPose = paths.nearStart;
+ public boolean turretEnabled = true;
+
+ @Override
+ public Pose getStartPose(){
+ return startPose;
+ }
+ @Override
+ public boolean getTurretEnabled(){
+ return turretEnabled;
+ }
+
+ @Override
+ public void runAuto(){
+ new SequentialGroup(
+ Commands.setIntakeMode(Intake.intakeMode.INTAKING),
+ new InstantCommand(Commands.setFlywheelState(Turret.flywheelState.ON)),
+ Commands.runPath(paths.nearStart_to_midShoot, true, 1),
+ Commands.shootBalls(),
+ Commands.setFlywheelState(Turret.flywheelState.ON),
+ Commands.setIntakeMode(Intake.intakeMode.INTAKING),
+ Commands.runPath(paths.midShoot_to_nearPreload, true, 1),
+ Commands.runPath(paths.nearPreload_to_midShoot, true, 1),
+ Commands.shootBalls(),
+ Commands.setFlywheelState(Turret.flywheelState.ON),
+ Commands.setIntakeMode(Intake.intakeMode.INTAKING),
+ Commands.runPath(paths.midShoot_to_midPreload, true, 1),
+ Commands.runPath(paths.midPreload_to_midShoot, true, 1),
+ Commands.shootBalls(),
+ Commands.runPath(paths.midShoot_to_nearLeave, true, 1)
+ ).schedule();
+ }
+
+ @NonNull
+ @Override
+ public String toString(){return "Near Zone 9 Ball Auto";}
+ }
+
+ private class Near_12Ball extends Auto{
+ public Pose startPose = paths.nearStart;
+ public boolean turretEnabled = true;
+
+ @Override
+ public Pose getStartPose(){
+ return startPose;
+ }
+ @Override
+ public boolean getTurretEnabled(){
+ return turretEnabled;
+ }
+
+ @Override
+ public void runAuto(){
+ new SequentialGroup(
+ Commands.setIntakeMode(Intake.intakeMode.INTAKING),
+ new InstantCommand(Commands.setFlywheelState(Turret.flywheelState.ON)),
+ Commands.runPath(paths.nearStart_to_midShoot, true, 1),
+ Commands.shootBalls(),
+ Commands.setFlywheelState(Turret.flywheelState.ON),
+ Commands.setIntakeMode(Intake.intakeMode.INTAKING),
+ Commands.runPath(paths.midShoot_to_nearPreload, true, 1),
+ Commands.runPath(paths.nearPreload_to_midShoot, true, 1),
+ Commands.shootBalls(),
+ Commands.setFlywheelState(Turret.flywheelState.ON),
+ Commands.setIntakeMode(Intake.intakeMode.INTAKING),
+ Commands.runPath(paths.midShoot_to_midPreload, true, 1),
+ Commands.runPath(paths.midPreload_to_midShoot, true, 1),
+ Commands.shootBalls(),
+ Commands.setFlywheelState(Turret.flywheelState.ON),
+ Commands.setIntakeMode(Intake.intakeMode.INTAKING),
+ Commands.runPath(paths.midShoot_to_farPreload, true, 1),
+ Commands.runPath(paths.farPreload_to_midShoot, true, 1),
+ Commands.shootBalls(),
+ Commands.runPath(paths.midShoot_to_nearLeave, true, 1)
+ ).schedule();
+ }
+
+ @NonNull
+ @Override
+ public String toString(){return "Near Zone 12 Ball Auto";}
+ }
+
+ /**
+ * This auto starts at the far zone
+ */
+ private class Complex_Test_Auto extends Auto{
+ public Pose startPose = paths.farStart;
+ public boolean turretEnabled = true;
+
+ @Override
+ public Pose getStartPose(){
+ return startPose;
+ }
+ @Override
+ public boolean getTurretEnabled(){
+ return turretEnabled;
+ }
+
+ @Override
+ void runAuto() {
+ new SequentialGroup(
+ new Delay(1),
+ //Commands.shootBalls(),
+ Commands.setIntakeMode(Intake.intakeMode.INTAKING),
+ Commands.runPath(paths.farStart_to_farPreload, true, 1),
+ Commands.setIntakeMode(Intake.intakeMode.OFF),
+ Commands.runPath(paths.farPreload_to_farShoot, true, 1),
+ //Commands.shootBalls(),
+ Commands.setIntakeMode(Intake.intakeMode.INTAKING),
+ Commands.runPath(paths.farStart_to_midPreload, true, 1),
+ Commands.setIntakeMode(Intake.intakeMode.OFF),
+ Commands.runPath(paths.midPreload_to_farShoot, true, 1),
+ //Commands.shootBalls(),
+ Commands.runPath(paths.farShoot_to_farLeave, true, 1)
+ ).schedule();
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "Complex Test Auto";
+ }
+ }
+
+ private class test_Auto extends Auto{
+ public Pose startPose = paths.farStart;
+ public boolean turretEnabled = false;
+
+ @Override
+ public Pose getStartPose(){
+ return startPose;
+ }
+ @Override
+ public boolean getTurretEnabled(){
+ return turretEnabled;
+ }
+
+ @Override
+ public void runAuto(){
+ double tempSpeed = 1;
+ new SequentialGroup(
+ Commands.runPath(paths.farStart_to_farPreload,true,tempSpeed),
+ Commands.runPath(paths.farPreload_to_farShoot,true,tempSpeed),
+ Commands.runPath(paths.farShoot_to_midPreload, true, tempSpeed),
+ Commands.runPath(paths.midPreload_to_midShoot, true, tempSpeed),
+ Commands.runPath(paths.midShoot_to_nearPreload, true, tempSpeed),
+ Commands.runPath(paths.nearPreload_to_nearShoot, true, tempSpeed)
+ ).schedule();
+ }
+
+ @NonNull
+ @Override
+ public String toString(){return "Test Auto";}
+ }
+}
diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Auto_/NextFTC_Pedro_Example_Auto.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Auto_/NextFTC_Pedro_Example_Auto.java
new file mode 100644
index 000000000000..bcbe78d74df4
--- /dev/null
+++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Auto_/NextFTC_Pedro_Example_Auto.java
@@ -0,0 +1,54 @@
+//package org.firstinspires.ftc.teamcode.LOADCode.Main_.Auto_;
+//
+//import com.acmerobotics.roadrunner.path.BezierLine;
+//import com.pedropathing.geometry.Pose;
+//import com.pedropathing.paths.PathChain;
+//import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
+//
+//import org.firstinspires.ftc.teamcode.pedroPathing.Constants;
+//
+//import dev.nextftc.core.commands.groups.SequentialGroup;
+//import dev.nextftc.core.components.SubsystemComponent;
+//import dev.nextftc.extensions.pedro.FollowPath;
+//import dev.nextftc.extensions.pedro.PedroComponent;
+//import dev.nextftc.ftc.NextFTCOpMode;
+//
+//@Autonomous(name = "blueBottom")
+//public class NextFTC_Pedro_Example_Auto extends NextFTCOpMode {
+// private final Pose startPose = new Pose(85.5, 8.3, Math.toRadians(90.0));
+// private final Pose depositPose = new Pose(84.3, 61.9, Math.toRadians(0.0));
+// private final Pose curvePoint = new Pose(138.2, 48.1);
+//
+// private PathChain skib;
+//
+// public NextFTC_Pedro_Example_Auto() {
+// addComponents(
+// new PedroComponent(Constants::createFollower)
+// );
+// }
+//
+// private void buildPaths() {
+// skib = follower.pathBuilder()
+// .addPath(new BezierLine(startPose, depositPose))
+// .setLinearHeadingInterpolation(startPose.heading, depositPose.heading)
+// .build();
+// }
+//
+// public Command getSecondRoutine() {
+// return new SequentialGroup(
+// new FollowPath(skib)
+// );
+// }
+//
+// @Override
+// public void onInit() {
+// follower.setMaxPower(0.7);
+// follower.setStartingPose(startPose);
+// buildPaths();
+// }
+//
+// @Override
+// public void onStartButtonPressed() {
+// getSecondRoutine().run();
+// }
+//}
\ No newline at end of file
diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Auto_/TestAuto.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Auto_/TestAuto.java
new file mode 100644
index 000000000000..3c1739e9678e
--- /dev/null
+++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Auto_/TestAuto.java
@@ -0,0 +1,254 @@
+package org.firstinspires.ftc.teamcode.LOADCode.Main_.Auto_; // make sure this aligns with class location
+
+import com.pedropathing.follower.Follower;
+import com.pedropathing.geometry.BezierCurve;
+import com.pedropathing.geometry.BezierLine;
+import com.pedropathing.geometry.Pose;
+import com.pedropathing.paths.PathChain;
+import com.pedropathing.util.Timer;
+import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
+import com.qualcomm.robotcore.eventloop.opmode.Disabled;
+import com.qualcomm.robotcore.eventloop.opmode.OpMode;
+import com.skeletonarmy.marrow.prompts.OptionPrompt;
+import com.skeletonarmy.marrow.prompts.Prompter;
+
+import org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.LoadHardwareClass;
+import org.firstinspires.ftc.teamcode.pedroPathing.Constants;
+
+@Disabled
+@Autonomous(name = "TestAutoR1", group = "TestAuto", preselectTeleOp="Teleop_Main_")
+public class TestAuto extends OpMode {
+
+ private Follower follower;
+ private Timer pathTimer, actionTimer, opmodeTimer;
+
+ private int pathState;
+ private boolean shooting;
+
+ Prompter prompter = null;
+
+ // Create a new instance of our Robot class
+ LoadHardwareClass Robot = new LoadHardwareClass(this);
+
+ private final Pose startPose = new Pose(87, 8.8, Math.toRadians(90)); // Start Pose of our robot.
+ private final Pose scorePose = new Pose(86, 22, Math.toRadians(80)); // Scoring Pose of our robot. It is facing the goal at a 135 degree angle.
+ private final Pose leavePose = new Pose(97, 30, Math.toRadians(80)); // Scoring Pose of our robot. It is facing the goal at a 135 degree angle.
+ private final Pose pickupR3PoseA = new Pose(110.4, 83.6, Math.toRadians(0)); // Highest (First Set) of Artifacts from the Spike Mark.
+ private final Pose pickupR3PoseB = new Pose(121.9, 83.6, Math.toRadians(0)); // Highest (First Set) of Artifacts from the Spike Mark.
+ private final Pose pickupR2PoseA = new Pose(110.4, 59.5, Math.toRadians(0)); // Middle (Second Set) of Artifacts from the Spike Mark.
+ private final Pose pickupR2PoseB = new Pose(121.9, 59.5, Math.toRadians(0)); // Middle (Second Set) of Artifacts from the Spike Mark.
+ private final Pose pickupR1PoseA = new Pose(110.4, 35.5, Math.toRadians(0)); // Lowest (Third Set) of Artifacts from the Spike Mark.
+ private final Pose pickupR1PoseB = new Pose(121.9, 35.5, Math.toRadians(0)); // Lowest (Third Set) of Artifacts from the Spike Mark.
+
+ private PathChain grabPickup1, scorePickup, grabPickup2, grabPickup3, leave;
+
+ public void buildPaths() {
+
+ grabPickup1 = follower.pathBuilder()
+ .addPath(new BezierCurve(
+ startPose,
+ new Pose(91.100, 35.700),
+ pickupR1PoseA
+ ))
+ .setLinearHeadingInterpolation(startPose.getHeading(), pickupR1PoseA.getHeading())
+ .addPath(new BezierLine(
+ pickupR1PoseA,
+ pickupR1PoseB
+ ))
+ .setLinearHeadingInterpolation(pickupR1PoseA.getHeading(), pickupR1PoseA.getHeading())
+ .build();
+
+ grabPickup2 = follower.pathBuilder()
+ .addPath(new BezierCurve(
+ scorePose,
+ new Pose(91.100, 60.5),
+ pickupR2PoseA
+ ))
+ .setLinearHeadingInterpolation(scorePose.getHeading(), pickupR2PoseA.getHeading())
+ .addPath(new BezierLine(
+ pickupR2PoseA,
+ pickupR2PoseB
+ ))
+ .setLinearHeadingInterpolation(pickupR2PoseA.getHeading(), pickupR2PoseA.getHeading())
+ .build();
+
+ grabPickup3 = follower.pathBuilder()
+ .addPath(new BezierCurve(
+ scorePose,
+ new Pose(91.100, 84.6),
+ pickupR3PoseA
+ ))
+ .setLinearHeadingInterpolation(scorePose.getHeading(), pickupR3PoseA.getHeading())
+ .addPath(new BezierLine(
+ pickupR3PoseA,
+ pickupR3PoseB
+ ))
+ .setLinearHeadingInterpolation(pickupR3PoseA.getHeading(), pickupR3PoseA.getHeading())
+ .build();
+
+ scorePickup = follower.pathBuilder()
+ .addPath(new BezierLine(
+ follower.getPose(),
+ scorePose
+ ))
+ .setLinearHeadingInterpolation(follower.getHeading(), scorePose.getHeading())
+ .build();
+ leave = follower.pathBuilder()
+ .addPath(new BezierLine(
+ follower.getPose(),
+ leavePose
+ ))
+ .setLinearHeadingInterpolation(follower.getHeading(), leavePose.getHeading())
+ .build();
+ }
+
+ /**
+ * Copied over from LinearOpMode.
+ * @param milliseconds The number of milliseconds to sleep.
+ */
+ public final void sleep(long milliseconds) {
+ try {
+ Thread.sleep(milliseconds);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ public void autonomousPathUpdate() {
+ switch (pathState) {
+ case 0:
+
+ /* You could check for
+ - Follower State: "if(!follower.isBusy()) {}"
+ - Time: "if(pathTimer.getElapsedTimeSeconds() > 1) {}"
+ - Robot Position: "if(follower.getPose().getX() > 36) {}"
+ */
+
+ /* This case checks the robot's position and will wait until the robot position is close (1 inch away) from the scorePose's position */
+ if(!follower.isBusy()) {
+ /* Since this is a pathChain, we can have Pedro hold the end point while we are grabbing the sample */
+ follower.followPath(grabPickup1,true);
+ setPathState(1);
+ }
+ break;
+ case 1:
+ /* This case checks the robot's position and will wait until the robot position is close (1 inch away) from the pickup1Pose's position */
+ if(!follower.isBusy()) {
+ /* Since this is a pathChain, we can have Pedro hold the end point while we are scoring the sample */
+ follower.followPath(scorePickup,true);
+ setPathState(2);
+ }
+ break;
+ case 2:
+ /* This case checks the robot's position and will wait until the robot position is close (1 inch away) from the scorePose's position */
+ if(!follower.isBusy()) {
+ /* Since this is a pathChain, we can have Pedro hold the end point while we are grabbing the sample */
+ follower.followPath(grabPickup2,true);
+ setPathState(3);
+ }
+ break;
+ case 3:
+ /* This case checks the robot's position and will wait until the robot position is close (1 inch away) from the pickup2Pose's position */
+ if(!follower.isBusy()) {
+ /* Since this is a pathChain, we can have Pedro hold the end point while we are scoring the sample */
+ follower.followPath(scorePickup,true);
+ setPathState(4);
+ }
+ break;
+ case 4:
+ /* This case checks the robot's position and will wait until the robot position is close (1 inch away) from the scorePose's position */
+ if(!follower.isBusy()) {
+ /* Since this is a pathChain, we can have Pedro hold the end point while we are grabbing the sample */
+ follower.followPath(grabPickup3,true);
+ setPathState(5);
+ }
+ break;
+ case 5:
+ /* This case checks the robot's position and will wait until the robot position is close (1 inch away) from the pickup3Pose's position */
+ if(!follower.isBusy()) {
+ /* Since this is a pathChain, we can have Pedro hold the end point while we are scoring the sample */
+ follower.followPath(scorePickup, true);
+ setPathState(6);
+ }
+ break;
+ case 6:
+ /* This case checks the robot's position and will wait until the robot position is close (1 inch away) from the pickup3Pose's position */
+ if(!follower.isBusy()) {
+ /* Since this is a pathChain, we can have Pedro hold the end point while we are scoring the sample */
+ follower.followPath(leave, true);
+ setPathState(7);
+ }
+ break;
+ case 7:
+ /* This case checks the robot's position and will wait until the robot position is close (1 inch away) from the scorePose's position */
+ if(!follower.isBusy()) {
+ /* Set the state to a Case we won't use or define, so it just stops running an new paths */
+ setPathState(-1);
+ }
+ break;
+ }
+ }
+
+ /** These change the states of the paths and actions. It will also reset the timers of the individual switches **/
+ public void setPathState(int pState) {
+ pathState = pState;
+ pathTimer.resetTimer();
+ }
+
+ /** This method is called once at the init of the OpMode. **/
+ @Override
+ public void init() {
+ pathTimer = new Timer();
+ opmodeTimer = new Timer();
+ opmodeTimer.resetTimer();
+
+ follower = Constants.createFollower(hardwareMap);
+ buildPaths();
+ follower.setStartingPose(startPose);
+
+ prompter = new Prompter(this);
+ prompter.prompt("alliance", new OptionPrompt<>("Select Alliance", LoadHardwareClass.Alliance.RED, LoadHardwareClass.Alliance.BLUE));
+ prompter.onComplete(() -> {
+ LoadHardwareClass.selectedAlliance = prompter.get("alliance");
+ telemetry.addData("Selection", "Complete");
+ }
+ );
+ }
+
+ /** This method is called continuously after Init while waiting for "play". **/
+ @Override
+ public void init_loop() {
+ prompter.run();
+ }
+
+ /** This method is called once at the start of the OpMode.
+ * It runs all the setup actions, including building paths and starting the path system **/
+ @Override
+ public void start() {
+ opmodeTimer.resetTimer();
+ setPathState(0);
+ }
+
+ /** This is the main loop of the OpMode, it will run repeatedly after clicking "Play". **/
+ @Override
+ public void loop() {
+
+ // These loop the movements of the robot, these must be called continuously in order to work
+ follower.update();
+ //autonomousPathUpdate();
+
+ telemetry.addData("Alliance", LoadHardwareClass.selectedAlliance);
+
+ // Feedback to Driver Hub for debugging
+ telemetry.addData("path state", pathState);
+ telemetry.addData("x", follower.getPose().getX());
+ telemetry.addData("y", follower.getPose().getY());
+ telemetry.addData("heading", follower.getPose().getHeading());
+ telemetry.update();
+ }
+
+ /** We do not use this because everything should automatically disable **/
+ @Override
+ public void stop() {
+ }
+}
\ No newline at end of file
diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Hardware_/Actuators_/Devices.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Hardware_/Actuators_/Devices.java
new file mode 100644
index 000000000000..1464fc9d6324
--- /dev/null
+++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Hardware_/Actuators_/Devices.java
@@ -0,0 +1,346 @@
+package org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.Actuators_;
+
+import androidx.annotation.NonNull;
+
+import com.qualcomm.robotcore.eventloop.opmode.OpMode;
+import com.qualcomm.robotcore.hardware.CRServo;
+import com.qualcomm.robotcore.hardware.DcMotor;
+import com.qualcomm.robotcore.hardware.DcMotorEx;
+import com.qualcomm.robotcore.hardware.DcMotorSimple;
+import com.qualcomm.robotcore.hardware.DigitalChannel;
+import com.qualcomm.robotcore.hardware.DistanceSensor;
+import com.qualcomm.robotcore.hardware.NormalizedColorSensor;
+import com.qualcomm.robotcore.hardware.NormalizedRGBA;
+import com.qualcomm.robotcore.hardware.Servo;
+
+import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
+
+import dev.nextftc.control.ControlSystem;
+import dev.nextftc.control.KineticState;
+import dev.nextftc.control.feedback.PIDCoefficients;
+import dev.nextftc.control.feedforward.BasicFeedforwardParameters;
+
+public class Devices {
+
+ public static class CRServoClass {
+ private CRServo servo;
+
+ public void init(@NonNull OpMode opmode, String servoName) {
+ servo = opmode.hardwareMap.get(CRServo.class, servoName);
+ }
+
+ /**
+ * @param power The power to set the servo to. Must be a value between -1 and 1.
+ */
+ public void setPower(double power) {
+ servo.setPower(power);
+ }
+
+ /**
+ * @param direction The direction to set the servo to.
+ */
+ public void setDirection(DcMotorSimple.Direction direction) {
+ servo.setDirection(direction);
+ }
+
+ /**
+ * @return The power the servo has been set to.
+ */
+ public double getPower() {
+ return servo.getPower();
+ }
+ }
+
+ public static class DcMotorExClass {
+
+ // Old PID Coefficients
+ PIDCoefficients old_pidCoefficients = new PIDCoefficients(0, 0, 0);
+ BasicFeedforwardParameters old_ffCoefficients = new BasicFeedforwardParameters(0,0,0);
+
+ // PID Coefficients
+ PIDCoefficients pidCoefficients = new PIDCoefficients(0, 0, 0);
+ BasicFeedforwardParameters ffCoefficients = new BasicFeedforwardParameters(0,0,0);
+ /**
+ *
intakeMode.INTAKINGintakeMode.SHOOTINGintakeMode.REVERSINGintakeMode.OFFintakeMode.INTAKINGintakeMode.SHOOTINGintakeMode.REVERSINGintakeMode.OFFgatestate.OPENgatestate.CLOSEDupdateFlywheel() must be called every loop for this to be effective.
+ * @param state The state to set the flywheel to (ON/OFF)
+ */
+ public void setFlywheelState(flywheelState state){
+ flywheelMode = state;
+ }
+
+ public double getFlywheelCurrentMaxSpeed(){
+ return targetRPM;
+ }
+
+
+ public boolean zeroTurret(){
+ if (!zeroed){
+ rotation.setPower(1);
+ if (hall.getTriggered()){
+ rotation.setPower(0);
+ rotation.resetEncoder();
+ zeroed = true;
+ }
+ }
+ opMode.telemetry.addLine("ZEROING TURRET");
+ opMode.telemetry.addData("Turret Power", rotation.getPower());
+ opMode.telemetry.update();
+ return !zeroed;
+ }
+
+ /**
+ * Updates the flywheel PID. Must be called every loop.
+ */
+ public void updateFlywheel(){
+ robotZone.setPosition(Robot.drivetrain.follower.getPose().getX(), Robot.drivetrain.follower.getPose().getY());
+ robotZone.setRotation(Robot.drivetrain.follower.getPose().getHeading());
+
+ opMode.telemetry.addData("In Far Zone", robotZone.isInside(LoadHardwareClass.FarLaunchZone));
+ opMode.telemetry.addData("In Near Zone", robotZone.isInside(LoadHardwareClass.ReallyNearLaunchZoneRed));
+
+ if (robotZone.isInside(LoadHardwareClass.FarLaunchZone)){
+ targetRPM = flywheelFarSpeed;
+ actualFlywheelCoefficients = flywheelCoefficients4200;
+ actualFlywheelFFCoefficients = flywheelFFCoefficients4200;
+ }else{
+ targetRPM = flywheelNearSpeed;
+ actualFlywheelCoefficients = flywheelCoefficients3500;
+ actualFlywheelFFCoefficients = flywheelFFCoefficients3500;
+ }
+
+ if (flywheelMode == flywheelState.ON){
+ setFlywheelRPM(targetRPM);
+ }else if (flywheelMode == flywheelState.OFF){
+ setFlywheelRPM(0);
+ }
+ }
+}
diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Hardware_/Commands.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Hardware_/Commands.java
new file mode 100644
index 000000000000..248a3e8939e8
--- /dev/null
+++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Hardware_/Commands.java
@@ -0,0 +1,95 @@
+package org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_;
+
+import androidx.annotation.NonNull;
+
+import com.pedropathing.paths.PathChain;
+import com.skeletonarmy.marrow.TimerEx;
+
+import org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.Actuators_.Intake;
+import org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.Actuators_.Turret;
+
+import dev.nextftc.core.commands.Command;
+import dev.nextftc.core.commands.delays.WaitUntil;
+import dev.nextftc.core.commands.groups.SequentialGroup;
+import dev.nextftc.core.commands.utility.InstantCommand;
+import dev.nextftc.core.commands.utility.LambdaCommand;
+import dev.nextftc.extensions.pedro.FollowPath;
+
+public class Commands {
+
+ // Robot Object for command access
+ public LoadHardwareClass Robot;
+ public Commands(@NonNull LoadHardwareClass robot){
+ Robot = robot;
+ }
+
+ // Delay timer for shooting sequence
+ private static final TimerEx shootingTimer = new TimerEx(1);
+ private static Command resetShootingTimer() {
+ return new LambdaCommand("resetShootingTimer").setStart(shootingTimer::restart);
+ }
+
+ public Command runPath(PathChain path, boolean holdEnd, double maxPower) {
+ return new FollowPath(path, holdEnd, maxPower);
+ }
+
+ public Command setFlywheelState(Turret.flywheelState state) {
+ return new LambdaCommand("setFlywheelState()")
+ .setInterruptible(false)
+ .setStart(() -> Robot.turret.setFlywheelState(state))
+ .setIsDone(() -> {
+ if (state == Turret.flywheelState.ON){
+ return Robot.turret.getFlywheelRPM() > Robot.turret.getFlywheelCurrentMaxSpeed() - 100;
+ }else{
+ return true;
+ }
+ })
+ ;
+ }
+
+ private Command setGateState(Turret.gatestate state){
+ return new InstantCommand(new LambdaCommand("setGateState")
+ .setStart(() -> Robot.turret.setGateState(state))
+ );
+ }
+
+ public Command setIntakeMode(Intake.intakeMode state) {
+ return new InstantCommand(new LambdaCommand("setIntakeMode()")
+ .setStart(() -> Robot.intake.setMode(state))
+ .setIsDone(() -> true)
+ );
+ }
+
+ public Command setTransferState(Intake.transferState state) {
+ return new InstantCommand(new LambdaCommand("setIntakeMode()")
+ .setStart(() -> Robot.intake.setTransfer(state))
+ .setIsDone(() -> true)
+ );
+ }
+
+ public Command shootBalls(){
+ return new SequentialGroup(
+ // Ensure the flywheel is up to speed, if not, spin up first
+ setFlywheelState(Turret.flywheelState.ON),
+
+ // Shoot the first two balls
+ setIntakeMode(Intake.intakeMode.INTAKING),
+ setGateState(Turret.gatestate.OPEN),
+ resetShootingTimer(),
+ new WaitUntil(() -> Robot.intake.getTopSensorState() && !Robot.intake.getBottomSensorState() && shootingTimer.isDone()),
+
+ // Shoot the last ball
+ setIntakeMode(Intake.intakeMode.SHOOTING),
+ setTransferState(Intake.transferState.UP),
+ resetShootingTimer(),
+ new WaitUntil(shootingTimer::isDone),
+
+ // Reset the systems
+ setIntakeMode(Intake.intakeMode.OFF),
+ setGateState(Turret.gatestate.CLOSED),
+ setTransferState(Intake.transferState.DOWN),
+ setFlywheelState(Turret.flywheelState.OFF)
+ );
+ }
+
+}
diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Hardware_/Drivetrain_/MecanumDrivetrainClass.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Hardware_/Drivetrain_/MecanumDrivetrainClass.java
new file mode 100644
index 000000000000..6f40ac67922b
--- /dev/null
+++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Hardware_/Drivetrain_/MecanumDrivetrainClass.java
@@ -0,0 +1,85 @@
+package org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.Drivetrain_;
+
+import androidx.annotation.NonNull;
+
+import com.pedropathing.follower.Follower;
+import com.pedropathing.geometry.Pose;
+import com.pedropathing.paths.PathChain;
+import com.qualcomm.robotcore.eventloop.opmode.OpMode;
+
+import org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.LoadHardwareClass;
+import org.firstinspires.ftc.teamcode.pedroPathing.Constants;
+
+public class MecanumDrivetrainClass {
+ // Controls the speed of the robot
+ public double speedMultiplier = 1.0; // make this slower for outreaches
+
+ // Misc Constants
+ public Follower follower = null;
+
+ public static Pose robotPose = null;
+
+ /**
+ * Initializes the PedroPathing follower.
+ * Needs to be run once after all hardware is initialized.
+ * @param myOpMode Allows the follower access to the robot hardware.
+ * @param initialPose The starting pose of the robot.
+ */
+ public void init (@NonNull OpMode myOpMode, Pose initialPose){
+ // PedroPathing initialization
+ follower = Constants.createFollower(myOpMode.hardwareMap); // Initializes the PedroPathing path follower
+ follower.setStartingPose(initialPose); // Sets the initial position of the robot on the field
+ follower.update(); // Applies the initialization
+ }
+
+ /**
+ * Initializes the PedroPathing follower.
+ * Needs to be run once after all hardware is initialized.
+ * @param myOpMode Allows the follower access to the robot hardware.
+ * @param initialPose The starting pose of the robot.
+ * @param follow The PedroPathing follower object
+ */
+ public void init (@NonNull OpMode myOpMode, Pose initialPose, Follower follow){
+ // PedroPathing initialization
+ follower = follow; // Initializes the PedroPathing path follower
+ follower.setPose(initialPose); // Sets the initial position of the robot on the field
+ follower.update(); // Applies the initialization
+ }
+
+ public void startTeleOpDrive(){
+ follower.startTeleopDrive();
+ follower.update();
+ }
+
+ /**
+ * Uses PedroPathing's follower class to implement a mecanum drive controller.
+ * Must be called every loop to function properly.
+ * @param forward The joystick value for driving forward/backward
+ * @param strafe The joystick value for strafing
+ * @param rotate The joystick value to turn left/right
+ * @param robotCentric If true, enables robot centric. If false, enables field centric.
+ */
+ public void pedroMecanumDrive(double forward, double strafe, double rotate, boolean robotCentric){
+ follower.setTeleOpDrive(
+ -forward * speedMultiplier,
+ -strafe * speedMultiplier,
+ -rotate * speedMultiplier,
+ robotCentric);
+ follower.update();
+ }
+
+ public void runPath(PathChain path, boolean holdEndpoint){
+ follower.followPath(path, holdEndpoint);
+ follower.update();
+ }
+
+ public double distanceFromGoal(){
+ Pose goalPose = new Pose(0,144,0);
+ if (LoadHardwareClass.selectedAlliance == LoadHardwareClass.Alliance.RED) {goalPose = new Pose(144, 144, 0);}
+ return follower.getPose().distanceFrom(goalPose);
+ }
+
+ public boolean pathComplete(){
+ return !follower.isBusy();
+ }
+}
\ No newline at end of file
diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Hardware_/Drivetrain_/Pedro_Paths.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Hardware_/Drivetrain_/Pedro_Paths.java
new file mode 100644
index 000000000000..5a18905eb4d1
--- /dev/null
+++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Main_/Hardware_/Drivetrain_/Pedro_Paths.java
@@ -0,0 +1,541 @@
+package org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.Drivetrain_;
+
+import com.pedropathing.follower.Follower;
+import com.pedropathing.geometry.BezierCurve;
+import com.pedropathing.geometry.BezierLine;
+import com.pedropathing.geometry.Pose;
+import com.pedropathing.paths.PathChain;
+
+import org.firstinspires.ftc.teamcode.LOADCode.Main_.Hardware_.LoadHardwareClass;
+
+public class Pedro_Paths {
+ // The variable to store PedroPathing's follower object for path building
+ private Follower follower;
+
+ /**
+ * Define primary poses to be used in paths
+ */
+ // Start Poses
+ public Pose nearStart = new Pose(118, 132, Math.toRadians(306));
+ public Pose farStart = new Pose(88, 7.4, Math.toRadians(90));
+ // Preload Poses
+ public Pose nearPreload = new Pose(127.000, 83.500, Math.toRadians(0));
+ public Pose midPreload = new Pose(132.000, 59.500, Math.toRadians(0));
+ public Pose farPreload = new Pose(132.000, 35.500, Math.toRadians(0));
+ // Shooting Poses
+ public Pose nearShoot = new Pose(115, 120, Math.toRadians(-35));
+ public Pose midShoot = new Pose(85, 85, Math.toRadians(-15));
+ public Pose farShoot = new Pose(85, 15, Math.toRadians(60));
+ public Pose noTurretMidShoot = new Pose(85, 85, Math.toRadians(45));
+ public Pose noTurretFarShoot = new Pose(85, 15, Math.toRadians(67.3));
+ // Leave Poses
+ public Pose nearLeave = new Pose(90,120, Math.toRadians(90));
+ public Pose midLeave = new Pose(95,55, Math.toRadians(90));
+ public Pose farLeave = new Pose(115,20, Math.toRadians(90));
+
+ /**
+ * Rotate Left/RightN/AStrafe Left/RightDrive Forwards/BackwardsSlow ModQuick ModN/AN/AN/AN/AN/AN/AN/AN/AN/AN/AN/AN/AN/AIntake INBelt INN/AN/AN/AToggle Turret Autoaim (Default on, locks to forward when offShootN/AFlywheel ToggleHood Up (Does not work currently)Hood Down (Does not work currently)Sets hood to ideal angle for far zone shootingN/ATransfer Belt OutTransfer Belt InN/AN/AIntake Reverse
+ * The spline is guaranteed to pass through each control point exactly. Moreover, assuming the control points are
+ * monotonic (Y is non-decreasing or non-increasing) then the interpolated values will also be monotonic.
+ *
+ * @throws IllegalArgumentException if the X or Y arrays are null, have different lengths or have fewer than 2 values.
+ */
+ //public static LUTWithInterpolator createLUT(ListTelemetryObject.
+ * There should only be one of these per program.
+ */
+ public class TelemetryManager{
+ ListTelemetryManager.
+ * It will contain all the relevant data for lookup and sorting, as well as functionality for live runtime editing of keys and values
+ */
+ public class TelemetryObject{
+
+ Object name;
+ Object key;
+ Object value;
+ telemetrySortCategory category;
+
+ public TelemetryObject(Object initialName,
+ Object initialKey,
+ Object initialValue,
+ telemetrySortCategory telemCategory,
+ TelemetryManager telemetryManagerParent){
+ name = initialName;
+ key = initialKey;
+ value = initialValue;
+ category = telemCategory;
+ }
+
+ public Object getName(){
+ return name;
+ }
+ public Object getKey(){
+ return key;
+ }
+ public Object getValue(){
+ return value;
+ }
+ public telemetrySortCategory getTelemCategory(){
+ return category;
+ }
+
+ }
+
+
+}
diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Notes and TODOs b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Notes and TODOs
new file mode 100644
index 000000000000..d5b4ec13edc1
--- /dev/null
+++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Notes and TODOs
@@ -0,0 +1,35 @@
+//TODO, implement all our external libraries and functionality.
+
+//DONE, Implement controls graph to TeleOp
+
+//DONE, Consolidate the generic actuator functionalities folder into one file
+
+//TODO, Organize telemetry before adding proper telem system
+
+//TODO, Add a more advanced telemetry handler for better organization, readability, and debugging
+
+//DONE, Add proper functionality for manual turret control to Turret.java
+
+//DONE, Shoot mode state machine Off->On->LastShot->Delay->ResetActuators->Off etc. cycled with B button on Gamepad2
+
+//TODO, Reset the belt, intake, & transfer kicker back to private after December 6th
+
+//TODO, add the new code structure graph, control graph, and gamepad HTML to the development branch Readme
+
+//DONE, Finish translating Dylan's December 6th controls into the program.
+
+//DONE, Figure out how to use NextFTC's Pedropathing extension for auto.
+
+
+
+//1. TODO, test the auto paths used in the generic autos - ARI WORKING ON IT!
+//2. TODO, create two generic autos for testing and competition
+//3. TODO, test the turret rotation autoaim with shooting
+//4. TODO, establish static (non-changing) flywheel speeds for near/far zones
+//5. TODO, make hood autoaim using InterpLUT
+//6. TODO, test turret hood and rotation autoaim together
+//7. TODO, test turret autoaim with the generic autos - ADD THE TURRET CONTROL FUNCTIONALITY TO COMMANDS
+
+// TODO: create paths to pick up human player zone Artifacts and to open the gate
+
+//TODO Link the PID update to the flywheel state so it can float instead of break at zero power
\ No newline at end of file
diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Testing_/PedroDriveToAprilTag.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Testing_/PedroDriveToAprilTag.java
new file mode 100644
index 000000000000..e61e6cbb6d4d
--- /dev/null
+++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/LOADCode/Testing_/PedroDriveToAprilTag.java
@@ -0,0 +1,291 @@
+/* Copyright (c) 2023 FIRST. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted (subject to the limitations in the disclaimer below) provided that
+ * the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * Neither the name of FIRST nor the names of its contributors may be used to endorse or
+ * promote products derived from this software without specific prior written permission.
+ *
+ * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS
+ * LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND NEAR ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.firstinspires.ftc.teamcode.LOADCode.Testing_;
+
+import com.bylazar.configurables.annotations.IgnoreConfigurable;
+import com.bylazar.telemetry.TelemetryManager;
+import com.pedropathing.follower.Follower;
+import com.pedropathing.geometry.Pose;
+import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
+import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
+import com.qualcomm.robotcore.util.Range;
+
+import org.firstinspires.ftc.robotcore.external.hardware.camera.BuiltinCameraDirection;
+import org.firstinspires.ftc.robotcore.external.hardware.camera.WebcamName;
+import org.firstinspires.ftc.robotcore.external.hardware.camera.controls.ExposureControl;
+import org.firstinspires.ftc.robotcore.external.hardware.camera.controls.GainControl;
+import org.firstinspires.ftc.teamcode.pedroPathing.Constants;
+import org.firstinspires.ftc.vision.VisionPortal;
+import org.firstinspires.ftc.vision.apriltag.AprilTagDetection;
+import org.firstinspires.ftc.vision.apriltag.AprilTagProcessor;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/*
+ * This OpMode illustrates using a camera to locate and drive towards a specific AprilTag.
+ * The code assumes a Holonomic (Mecanum or X Drive) Robot.
+ *
+ * For an introduction to AprilTags, see the ftc-docs link below:
+ * https://ftc-docs.firstinspires.org/en/latest/apriltag/vision_portal/apriltag_intro/apriltag-intro.html
+ *
+ * When an AprilTag in the TagLibrary is detected, the SDK provides location and orientation of the tag, relative to the camera.
+ * This information is provided in the "ftcPose" member of the returned "detection", and is explained in the ftc-docs page linked below.
+ * https://ftc-docs.firstinspires.org/apriltag-detection-values
+ *
+ * The drive goal is to rotate to keep the Tag centered in the camera, while strafing to be directly in front of the tag, and
+ * driving towards the tag to achieve the desired distance.
+ * To reduce any motion blur (which will interrupt the detection process) the Camera exposure is reduced to a very low value (5mS)
+ * You can determine the best Exposure and Gain values by using the ConceptAprilTagOptimizeExposure OpMode in this Samples folder.
+ *
+ * The code assumes a Robot Configuration with motors named: front_left_drive and front_right_drive, back_left_drive and back_right_drive.
+ * The motor directions must be set so a positive power goes forward on all wheels.
+ * This sample assumes that the current game AprilTag Library (usually for the current season) is being loaded by default,
+ * so you should choose to approach a valid tag ID.
+ *
+ * Under manual control, the left stick will move forward/back & left/right. The right stick will rotate the robot.
+ * Manually drive the robot until it displays Target data on the Driver Station.
+ *
+ * Press and hold the *Left Bumper* to enable the automatic "Drive to target" mode.
+ * Release the Left Bumper to return to manual driving mode.
+ *
+ * Under "Drive To Target" mode, the robot has three goals:
+ * 1) Turn the robot to always keep the Tag centered on the camera frame. (Use the Target Bearing to turn the robot.)
+ * 2) Strafe the robot towards the centerline of the Tag, so it approaches directly in front of the tag. (Use the Target Yaw to strafe the robot)
+ * 3) Drive towards the Tag to get to the desired distance. (Use Tag Range to drive the robot forward/backward)
+ *
+ * Use DESIRED_DISTANCE to set how close you want the robot to get to the target.
+ * Speed and Turn sensitivity can be adjusted using the SPEED_GAIN, STRAFE_GAIN and TURN_GAIN constants.
+ *
+ * Use Android Studio to Copy this Class, and Paste it into the TeamCode/src/main/java/org/firstinspires/ftc/teamcode folder.
+ * Remove or comment out the @Disabled line to add this OpMode to the Driver Station OpMode list.
+ *
+ */
+@TeleOp(name="Follow AprilTag", group = "TestTeleOp")
+public class PedroDriveToAprilTag extends LinearOpMode
+{
+ // Adjust these numbers to suit your robot.
+ final double DESIRED_DISTANCE = 60.0; // this is how close the camera should get to the target (inches)
+
+ // Set the GAIN constants to control the relationship between the measured position error, and how much power is
+ // applied to the drive motors to correct the error.
+ // Drive = Error * Gain Make these values smaller for smoother control, or larger for a more aggressive response.
+ final double SPEED_GAIN = 0.04 ; // Forward Speed Control "Gain". e.g. Ramp up to 50% power at a 25 inch error. (0.50 / 25.0)
+ final double STRAFE_GAIN = 0.04 ; // Strafe Speed Control "Gain". e.g. Ramp up to 37% power at a 25 degree Yaw error. (0.375 / 25.0)
+ final double TURN_GAIN = 0.04 ; // Turn Control "Gain". e.g. Ramp up to 25% power at a 25 degree error. (0.25 / 25.0)
+
+ final double MAX_AUTO_SPEED = 1; // Clip the approach speed to this max value (adjust for your robot)
+ final double MAX_AUTO_STRAFE= 1; // Clip the strafing speed to this max value (adjust for your robot)
+ final double MAX_AUTO_TURN = 1; // Clip the turn speed to this max value (adjust for your robot)
+
+ private static final boolean USE_WEBCAM = true; // Set true to use a webcam, or false for a phone camera
+ private static final int DESIRED_TAG_ID = -1; // Choose the tag you want to approach or set to -1 for ANY tag.
+ private VisionPortal visionPortal; // Used to manage the video source.
+ private AprilTagProcessor aprilTag; // Used for managing the AprilTag detection process.
+ private AprilTagDetection desiredTag = null; // Used to hold the data for a detected AprilTag
+
+ private boolean toggle = false;
+ private boolean oldToggleVal = false;
+
+ public static Follower follower;
+ @IgnoreConfigurable
+ static TelemetryManager telemetryM;
+ /** Start Pose of our robot. This can be changed or saved from the autonomous period. */
+ private final Pose startPose = new Pose(0,0, Math.toRadians(0));
+
+ @Override public void runOpMode()
+ {
+ boolean targetFound; // Set to true when an AprilTag target is detected
+ double drive; // Desired forward power/speed (-1 to +1)
+ double strafe; // Desired strafe power/speed (-1 to +1)
+ double turn; // Desired turning power/speed (-1 to +1)
+
+ // Initialize the Apriltag Detection process
+ initAprilTag();
+
+ if (USE_WEBCAM)
+ setManualExposure(6, 250); // Use low exposure time to reduce motion blur
+
+ follower = Constants.createFollower(hardwareMap);
+ follower.setStartingPose(startPose);
+ follower.update();
+
+ // Wait for driver to press start
+ telemetry.addData("Camera preview on/off", "3 dots, Camera Stream");
+ telemetry.addData(">", "Touch START to start OpMode");
+ telemetry.update();
+ waitForStart();
+
+ follower.startTeleopDrive();
+ follower.update();
+
+ while (opModeIsActive())
+ {
+ targetFound = false;
+ desiredTag = null;
+
+ // Step through the list of detected tags and look for a matching tag
+ List