├── .gitignore ├── README.md ├── _config.yml ├── build.gradle ├── doc └── images │ ├── build-common-gradle.png │ ├── gradle-sync.png │ ├── gradledepend.png │ ├── jcenter.png │ └── teamcode-gradle.png ├── examples ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── org │ └── openftc │ └── revextensions2 │ └── examples │ ├── BulkDataExample.java │ ├── CurrentMonitorsExample.java │ ├── HwSwInfoExample.java │ ├── MiscExamples.java │ ├── ServoPulseWidthExample.java │ ├── TemperatureMonitorsExample.java │ └── VoltageMonitorsExample.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── index.md ├── release-aar.gradle ├── rev-extensions-2 ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── org │ └── openftc │ └── revextensions2 │ ├── ExpansionHubEx.java │ ├── ExpansionHubMotor.java │ ├── ExpansionHubServo.java │ ├── RE2Exception.java │ ├── RevBulkData.java │ ├── RevBulkDataException.java │ ├── RevExtensions2.java │ ├── Utils.java │ └── VexMC29.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/ 38 | 39 | # Keystore files 40 | # Uncomment the following line if you do not want to check your keystore files in. 41 | #*.jks 42 | 43 | # External native build folder generated in Android Studio 2.2 and later 44 | .externalNativeBuild 45 | 46 | # Google Services (e.g. APIs or Firebase) 47 | google-services.json 48 | 49 | # Freeline 50 | freeline.py 51 | freeline/ 52 | freeline_project_description.json 53 | 54 | # fastlane 55 | fastlane/report.xml 56 | fastlane/Preview.html 57 | fastlane/screenshots 58 | fastlane/test_output 59 | fastlane/readme.md 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RevExtensions2 2 | 3 | **NOTE: SDK v4.x/5.x is required to use this** 4 | 5 | **NOTE: an OpenRC-based SDK is NOT required to use this** 6 | 7 | **NOTE: scroll down for installation instructions** 8 | 9 | ### DISCLAIMER: 10 | 11 | Use this at your own risk. **You accept the possibility that using these extra features may cause the SDK to implode and cause you lose F-3 at worlds.** 12 | That being said, I used a few of these features in competition this past season without any issues. 13 | 14 | ## What extra functionality does this add to the SDK? 15 | 16 | - Setting the LED color on the Hub 17 | - Setting the speed of the I2C buses on the Hub 18 | - Querying for total module current draw 19 | - Querying for servo bus current draw [ **broken:** REV firmware bug ] 20 | - Querying for GPIO bus current draw 21 | - Querying for I2C bus current draw 22 | - Querying for individual motor channel current draw 23 | - Reading the 5v monitor 24 | - Reading the 12v monitor 25 | - Queying for the internal temperature inside the Hub [ **broken:** unknown units ] 26 | - Query whether the Expansion Hub is in an over-temp condition 27 | - Query whether each individual motor H-bridge is over-temp 28 | - Query whether phone charging is enabled [ **broken:** REV firmware bug ] 29 | - Enable or disable phone charging 30 | - Setting servo pulse width directly in microseconds 31 | - Reading certain data in bulk from the Hub (can increase your control loop speed) 32 | 33 | The following data can be read in one go: 34 | 35 | - All 4 encoder counts 36 | - All 4 encoder velocities 37 | - All digital pins 38 | - All all analog pins 39 | - Is motor at target position for all 4 motors 40 | 41 | - Query whether a motor has "lost counts" [ **broken:** no idea what this actually does ] 42 | - Query for which firmware version the Hub has installed 43 | - Query for which hardware revision the Hub is 44 | - Convenient `VexMC29` class to prevent the need to manually scale the servo range for the Vex MC29. 45 | 46 | ## How do I install this? 47 | 48 | 1. Open your FTC SDK Android Studio project 49 | 2. Open the `build.common.gradle` file: 50 | 51 | ![img-her](doc/images/build-common-gradle.png) 52 | 53 | 3. Add `jcenter()` to the `repositories` block at the bottom: 54 | 55 | ![img-her](doc/images/jcenter.png) 56 | 57 | 4. Open the `build.gradle` file for the TeamCode module: 58 | 59 | ![img-her](doc/images/teamcode-gradle.png) 60 | 61 | 5. At the bottom, add this: 62 | 63 | dependencies { 64 | //NOTE: this is the correct version, the below screenshot is outdated 65 | implementation 'org.openftc:rev-extensions-2:1.2' 66 | } 67 | 68 | When you've done that, the bottom of the file should look like this: 69 | 70 | ![img-her](doc/images/gradledepend.png) 71 | 72 | 6. Now perform a Gradle Sync: 73 | 74 | ![img-her](doc/images/gradle-sync.png) 75 | 76 | 7. Wait for Gradle to finish gradling 77 | 78 | 8. Congratulations! You are now ready to use the new features provided by REV Extensions 2! 79 | 80 | 81 | ## Ok now that I've installed it, how do I use this? 82 | 83 | Please see the [examples](examples/src/main/java/org/openftc/revextensions2/examples) folder. More detailed documentation coming soon. 84 | 85 | ## Changelog 86 | 87 | ### v1.2 88 | 89 | - Fix compatibility with SDK v5.x (should still be backwards compatible with v4.x, as the difference was not a compile-time issue) 90 | - **Bugfix:** (Issue #6) Prevent possibility of exception when doing bulk reads during disconnect event 91 | - Add `VexMC29` class, which scales the normal -1 to 1 CRServo PWM range to the range expected by the MC29. 92 | - **API change:** The hardware map is now hotswapped transparently - you no longer need to call `RevExtensions2.init()` 93 | - **API change:** Require units as a parameter to get voltage/current/temperature methods 94 | 95 | ### v1.1 96 | 97 | - Fix compatibility with SDK v4.x (due to API breaking changes, older versions of the SDK are no longer supported) 98 | - Fix `RevBulkData.getMotorCurrentPosition()` and `RevBulkData.getMotorVelocity()` to return the value signed to be consistent with `DcMotor.getMotorCurrentPosition()` and `DcMotorEx.getVelocity()` 99 | - Correct typo in `CurrentMonitorsExample.java` 100 | - Don't ignore reflection exceptions 101 | - Add JavaDoc comments to `RevBulkData.java` 102 | - Refactor hotswapping slightly 103 | 104 | ### v1.0 105 | 106 | - Initial release 107 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-hacker -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.0.1' 11 | 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | maven { 23 | url 'https://dl.bintray.com/openftc/maven'; 24 | } 25 | } 26 | } 27 | 28 | task clean(type: Delete) { 29 | delete rootProject.buildDir 30 | } 31 | -------------------------------------------------------------------------------- /doc/images/build-common-gradle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFTC/RevExtensions2/e98d1a55e58353d72985f1871c01adcc0781dfbb/doc/images/build-common-gradle.png -------------------------------------------------------------------------------- /doc/images/gradle-sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFTC/RevExtensions2/e98d1a55e58353d72985f1871c01adcc0781dfbb/doc/images/gradle-sync.png -------------------------------------------------------------------------------- /doc/images/gradledepend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFTC/RevExtensions2/e98d1a55e58353d72985f1871c01adcc0781dfbb/doc/images/gradledepend.png -------------------------------------------------------------------------------- /doc/images/jcenter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFTC/RevExtensions2/e98d1a55e58353d72985f1871c01adcc0781dfbb/doc/images/jcenter.png -------------------------------------------------------------------------------- /doc/images/teamcode-gradle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFTC/RevExtensions2/e98d1a55e58353d72985f1871c01adcc0781dfbb/doc/images/teamcode-gradle.png -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /examples/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 26 5 | 6 | defaultConfig { 7 | minSdkVersion 19 8 | targetSdkVersion 26 9 | versionCode 1 10 | versionName "1.0" 11 | 12 | } 13 | 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | 21 | } 22 | 23 | dependencies { 24 | //implementation fileTree(include: ['*.aar'], dir: '../libs') 25 | compileOnly 'org.openftc:hardware-stock:5.0' 26 | compileOnly 'org.openftc:robotcore-stock:5.0' 27 | compileOnly project(':rev-extensions-2') 28 | } 29 | -------------------------------------------------------------------------------- /examples/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /examples/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /examples/src/main/java/org/openftc/revextensions2/examples/BulkDataExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | package org.openftc.revextensions2.examples; 23 | 24 | import com.qualcomm.robotcore.eventloop.opmode.OpMode; 25 | import com.qualcomm.robotcore.eventloop.opmode.TeleOp; 26 | import com.qualcomm.robotcore.hardware.AnalogInput; 27 | import com.qualcomm.robotcore.hardware.DigitalChannel; 28 | 29 | import org.openftc.revextensions2.ExpansionHubEx; 30 | import org.openftc.revextensions2.ExpansionHubMotor; 31 | import org.openftc.revextensions2.RevBulkData; 32 | 33 | @TeleOp(group = "RevExtensions2Examples") 34 | public class BulkDataExample extends OpMode 35 | { 36 | RevBulkData bulkData; 37 | AnalogInput a0, a1, a2, a3; 38 | DigitalChannel d0, d1, d2, d3, d4, d5, d6, d7; 39 | ExpansionHubMotor motor0, motor1, motor2, motor3; 40 | ExpansionHubEx expansionHub; 41 | 42 | @Override 43 | public void init() 44 | { 45 | /* 46 | * Before init() was called on this user code, REV Extensions 2 47 | * was notified via OpModeManagerNotifier.Notifications and 48 | * it automatically took care of initializing the new objects 49 | * in the hardwaremap for you. Historically, you would have 50 | * needed to call RevExtensions2.init() 51 | */ 52 | expansionHub = hardwareMap.get(ExpansionHubEx.class, "Expansion Hub 2"); 53 | motor0 = (ExpansionHubMotor) hardwareMap.dcMotor.get("motor0"); 54 | motor1 = (ExpansionHubMotor) hardwareMap.dcMotor.get("motor1"); 55 | motor2 = (ExpansionHubMotor) hardwareMap.dcMotor.get("motor2"); 56 | motor3 = (ExpansionHubMotor) hardwareMap.dcMotor.get("motor3"); 57 | a0 = hardwareMap.analogInput.get("a0"); 58 | a1 = hardwareMap.analogInput.get("a1"); 59 | a2 = hardwareMap.analogInput.get("a2"); 60 | a3 = hardwareMap.analogInput.get("a3"); 61 | d0 = hardwareMap.digitalChannel.get("d0"); 62 | d1 = hardwareMap.digitalChannel.get("d1"); 63 | d2 = hardwareMap.digitalChannel.get("d2"); 64 | d3 = hardwareMap.digitalChannel.get("d3"); 65 | d4 = hardwareMap.digitalChannel.get("d4"); 66 | d5 = hardwareMap.digitalChannel.get("d5"); 67 | d6 = hardwareMap.digitalChannel.get("d6"); 68 | d7 = hardwareMap.digitalChannel.get("d7"); 69 | } 70 | 71 | @Override 72 | public void loop() 73 | { 74 | /* 75 | * ------------------------------------------------------------------------------------------------ 76 | * Bulk data 77 | * 78 | * NOTE: While you could get all of this information by issuing many separate commands, 79 | * the amount of time required to fetch the information would increase drastically. By 80 | * reading all of this information in one command, we can loop at over 300Hz (!!!) 81 | * ------------------------------------------------------------------------------------------------ 82 | */ 83 | 84 | bulkData = expansionHub.getBulkInputData(); 85 | 86 | String header = 87 | "**********************************\n" + 88 | "BULK DATA EXAMPLE \n" + 89 | "**********************************\n"; 90 | telemetry.addLine(header); 91 | 92 | /* 93 | * Encoders 94 | */ 95 | telemetry.addData("M0 enocder", bulkData.getMotorCurrentPosition(motor0)); 96 | telemetry.addData("M1 encoder", bulkData.getMotorCurrentPosition(motor1)); 97 | telemetry.addData("M2 encoder", bulkData.getMotorCurrentPosition(motor2)); 98 | telemetry.addData("M3 encoder", bulkData.getMotorCurrentPosition(motor3)); 99 | 100 | /* 101 | * Encoder velocities 102 | */ 103 | telemetry.addData("M0 velocity", bulkData.getMotorVelocity(motor0)); 104 | telemetry.addData("M1 velocity", bulkData.getMotorVelocity(motor1)); 105 | telemetry.addData("M2 velocity", bulkData.getMotorVelocity(motor2)); 106 | telemetry.addData("M3 velocity", bulkData.getMotorVelocity(motor3)); 107 | 108 | /* 109 | * Is motor at target position? 110 | */ 111 | telemetry.addData("M0 at target pos", bulkData.isMotorAtTargetPosition(motor0)); 112 | telemetry.addData("M1 at target pos", bulkData.isMotorAtTargetPosition(motor1)); 113 | telemetry.addData("M2 at target pos", bulkData.isMotorAtTargetPosition(motor2)); 114 | telemetry.addData("M3 at target pos", bulkData.isMotorAtTargetPosition(motor3)); 115 | 116 | /* 117 | * Analog voltages 118 | */ 119 | telemetry.addData("A0", bulkData.getAnalogInputValue(a0)); 120 | telemetry.addData("A1", bulkData.getAnalogInputValue(a1)); 121 | telemetry.addData("A2", bulkData.getAnalogInputValue(a2)); 122 | telemetry.addData("A3", bulkData.getAnalogInputValue(a3)); 123 | 124 | /* 125 | * Digital states 126 | */ 127 | telemetry.addData("D0", bulkData.getDigitalInputState(d0)); 128 | telemetry.addData("D1", bulkData.getDigitalInputState(d1)); 129 | telemetry.addData("D2", bulkData.getDigitalInputState(d2)); 130 | telemetry.addData("D3", bulkData.getDigitalInputState(d3)); 131 | telemetry.addData("D4", bulkData.getDigitalInputState(d4)); 132 | telemetry.addData("D5", bulkData.getDigitalInputState(d5)); 133 | telemetry.addData("D6", bulkData.getDigitalInputState(d6)); 134 | telemetry.addData("D7", bulkData.getDigitalInputState(d7)); 135 | 136 | telemetry.update(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /examples/src/main/java/org/openftc/revextensions2/examples/CurrentMonitorsExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | package org.openftc.revextensions2.examples; 23 | 24 | import com.qualcomm.robotcore.eventloop.opmode.OpMode; 25 | import com.qualcomm.robotcore.eventloop.opmode.TeleOp; 26 | 27 | import org.openftc.revextensions2.ExpansionHubEx; 28 | import org.openftc.revextensions2.ExpansionHubMotor; 29 | 30 | @TeleOp(group = "RevExtensions2Examples") 31 | public class CurrentMonitorsExample extends OpMode 32 | { 33 | ExpansionHubMotor motor0, motor1, motor2, motor3; 34 | ExpansionHubEx expansionHub; 35 | 36 | @Override 37 | public void init() 38 | { 39 | /* 40 | * Before init() was called on this user code, REV Extensions 2 41 | * was notified via OpModeManagerNotifier.Notifications and 42 | * it automatically took care of initializing the new objects 43 | * in the hardwaremap for you. Historically, you would have 44 | * needed to call RevExtensions2.init() 45 | */ 46 | expansionHub = hardwareMap.get(ExpansionHubEx.class, "Expansion Hub 2"); 47 | motor0 = (ExpansionHubMotor) hardwareMap.dcMotor.get("motor0"); 48 | motor1 = (ExpansionHubMotor) hardwareMap.dcMotor.get("motor1"); 49 | motor2 = (ExpansionHubMotor) hardwareMap.dcMotor.get("motor2"); 50 | motor3 = (ExpansionHubMotor) hardwareMap.dcMotor.get("motor3"); 51 | } 52 | 53 | @Override 54 | public void loop() 55 | { 56 | /* 57 | * ------------------------------------------------------------------------------------------------ 58 | * Current monitors - NOTE: units are milliamps 59 | * ------------------------------------------------------------------------------------------------ 60 | */ 61 | 62 | String header = 63 | "**********************************\n" + 64 | "CURRENT MONITORING EXAMPLE \n" + 65 | "NOTE: UNITS ARE AMPS \n" + 66 | "**********************************\n"; 67 | telemetry.addLine(header); 68 | 69 | telemetry.addData("Total current", expansionHub.getTotalModuleCurrentDraw(ExpansionHubEx.CurrentDrawUnits.AMPS)); 70 | telemetry.addData("I2C current", expansionHub.getI2cBusCurrentDraw(ExpansionHubEx.CurrentDrawUnits.AMPS)); 71 | telemetry.addData("GPIO current", expansionHub.getGpioBusCurrentDraw(ExpansionHubEx.CurrentDrawUnits.AMPS)); 72 | telemetry.addData("M0 current", motor0.getCurrentDraw(ExpansionHubEx.CurrentDrawUnits.AMPS)); 73 | telemetry.addData("M1 current", motor1.getCurrentDraw(ExpansionHubEx.CurrentDrawUnits.AMPS)); 74 | telemetry.addData("M2 current", motor2.getCurrentDraw(ExpansionHubEx.CurrentDrawUnits.AMPS)); 75 | telemetry.addData("M3 current", motor3.getCurrentDraw(ExpansionHubEx.CurrentDrawUnits.AMPS)); 76 | 77 | telemetry.update(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /examples/src/main/java/org/openftc/revextensions2/examples/HwSwInfoExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | package org.openftc.revextensions2.examples; 23 | 24 | import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode; 25 | import com.qualcomm.robotcore.eventloop.opmode.TeleOp; 26 | 27 | import org.openftc.revextensions2.ExpansionHubEx; 28 | 29 | @TeleOp(group = "RevExtensions2Examples") 30 | public class HwSwInfoExample extends LinearOpMode 31 | { 32 | ExpansionHubEx expansionHub; 33 | 34 | @Override 35 | public void runOpMode() throws InterruptedException 36 | { 37 | /* 38 | * Before init() was called on this user code, REV Extensions 2 39 | * was notified via OpModeManagerNotifier.Notifications and 40 | * it automatically took care of initializing the new objects 41 | * in the hardwaremap for you. Historically, you would have 42 | * needed to call RevExtensions2.init() 43 | */ 44 | expansionHub = hardwareMap.get(ExpansionHubEx.class, "Expansion Hub 2"); 45 | 46 | /* 47 | * ------------------------------------------------------------------------------------------------ 48 | * HW/SW info 49 | * ------------------------------------------------------------------------------------------------ 50 | */ 51 | 52 | String header = 53 | "**********************************\n" + 54 | "HW/SW INFO EXAMPLE \n" + 55 | "**********************************\n"; 56 | telemetry.addLine(header); 57 | 58 | telemetry.addData("Firmware", expansionHub.getFirmwareVersion()); 59 | telemetry.addData("Hardware revision", expansionHub.getHardwareRevision()); 60 | telemetry.update(); 61 | 62 | waitForStart(); 63 | while (opModeIsActive()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /examples/src/main/java/org/openftc/revextensions2/examples/MiscExamples.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | package org.openftc.revextensions2.examples; 23 | 24 | import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode; 25 | import com.qualcomm.robotcore.eventloop.opmode.TeleOp; 26 | 27 | import org.openftc.revextensions2.ExpansionHubEx; 28 | 29 | @TeleOp(group = "RevExtensions2Examples") 30 | public class MiscExamples extends LinearOpMode 31 | { 32 | ExpansionHubEx expansionHub; 33 | 34 | @Override 35 | public void runOpMode() throws InterruptedException 36 | { 37 | /* 38 | * Before init() was called on this user code, REV Extensions 2 39 | * was notified via OpModeManagerNotifier.Notifications and 40 | * it automatically took care of initializing the new objects 41 | * in the hardwaremap for you. Historically, you would have 42 | * needed to call RevExtensions2.init() 43 | */ 44 | expansionHub = hardwareMap.get(ExpansionHubEx.class, "Expansion Hub 2"); 45 | 46 | String header = 47 | "**********************************\n" + 48 | "MISC. EXAMPLES \n" + 49 | "**********************************\n"; 50 | telemetry.addLine(header); 51 | 52 | /* 53 | * Setting ExpansionHub LED color 54 | */ 55 | expansionHub.setLedColor(255, 0, 0); 56 | telemetry.addLine("Setting Hub LED color"); 57 | 58 | /* 59 | * Setting ExpansionHub I2C bus speed 60 | */ 61 | expansionHub.setAllI2cBusSpeeds(ExpansionHubEx.I2cBusSpeed.FAST_400K); 62 | telemetry.addLine("Setting speed of all I2C buses"); 63 | 64 | telemetry.update(); 65 | 66 | waitForStart(); 67 | while (opModeIsActive()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/src/main/java/org/openftc/revextensions2/examples/ServoPulseWidthExample.java: -------------------------------------------------------------------------------- 1 | package org.openftc.revextensions2.examples; 2 | 3 | import com.qualcomm.robotcore.eventloop.opmode.OpMode; 4 | import com.qualcomm.robotcore.eventloop.opmode.TeleOp; 5 | 6 | import org.openftc.revextensions2.ExpansionHubServo; 7 | 8 | @TeleOp(group = "RevExtensions2Examples") 9 | public class ServoPulseWidthExample extends OpMode 10 | { 11 | ExpansionHubServo servo; 12 | 13 | @Override 14 | public void init() 15 | { 16 | /* 17 | * Before init() was called on this user code, REV Extensions 2 18 | * was notified via OpModeManagerNotifier.Notifications and 19 | * it automatically took care of initializing the new objects 20 | * in the hardwaremap for you. Historically, you would have 21 | * needed to call RevExtensions2.init() 22 | */ 23 | servo = (ExpansionHubServo) hardwareMap.servo.get("servo"); 24 | } 25 | 26 | @Override 27 | public void loop() 28 | { 29 | String header = 30 | "**********************************\n" + 31 | "SERVO PULSE WIDTH EXAMPLE \n" + 32 | "**********************************\n"; 33 | telemetry.addLine(header); 34 | 35 | servo.setPwmEnable(); // <-- IMPORTANT: make sure the servo is enabled, otherwise setPulseWidthUs() will appear to have no effect 36 | servo.setPulseWidthUs(1000); 37 | 38 | telemetry.addLine("Setting servo pulse width directly..."); 39 | telemetry.update(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/src/main/java/org/openftc/revextensions2/examples/TemperatureMonitorsExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | package org.openftc.revextensions2.examples; 23 | 24 | import com.qualcomm.robotcore.eventloop.opmode.OpMode; 25 | import com.qualcomm.robotcore.eventloop.opmode.TeleOp; 26 | 27 | import org.openftc.revextensions2.ExpansionHubEx; 28 | import org.openftc.revextensions2.ExpansionHubMotor; 29 | 30 | @TeleOp(group = "RevExtensions2Examples") 31 | public class TemperatureMonitorsExample extends OpMode 32 | { 33 | ExpansionHubMotor motor0, motor1, motor2, motor3; 34 | ExpansionHubEx expansionHub; 35 | 36 | @Override 37 | public void init() 38 | { 39 | /* 40 | * Before init() was called on this user code, REV Extensions 2 41 | * was notified via OpModeManagerNotifier.Notifications and 42 | * it automatically took care of initializing the new objects 43 | * in the hardwaremap for you. Historically, you would have 44 | * needed to call RevExtensions2.init() 45 | */ 46 | expansionHub = hardwareMap.get(ExpansionHubEx.class, "Expansion Hub 2"); 47 | motor0 = (ExpansionHubMotor) hardwareMap.dcMotor.get("motor0"); 48 | motor1 = (ExpansionHubMotor) hardwareMap.dcMotor.get("motor1"); 49 | motor2 = (ExpansionHubMotor) hardwareMap.dcMotor.get("motor2"); 50 | motor3 = (ExpansionHubMotor) hardwareMap.dcMotor.get("motor3"); 51 | } 52 | 53 | @Override 54 | public void loop() 55 | { 56 | /* 57 | * ------------------------------------------------------------------------------------------------ 58 | * Temperature monitors 59 | * ------------------------------------------------------------------------------------------------ 60 | */ 61 | 62 | String header = 63 | "**********************************\n" + 64 | "TEMPERATURE MONITORS EXAMPLE \n" + 65 | "**********************************\n"; 66 | telemetry.addLine(header); 67 | 68 | telemetry.addData("Module temp", expansionHub.getInternalTemperature(ExpansionHubEx.TemperatureUnits.FAHRENHEIT) + "F"); 69 | telemetry.addData("Module over temp", expansionHub.isModuleOverTemp()); 70 | telemetry.addData("M0 H-bridge over temp", motor0.isBridgeOverTemp()); 71 | telemetry.addData("M1 H-bridge over temp", motor1.isBridgeOverTemp()); 72 | telemetry.addData("M2 H-bridge over temp", motor2.isBridgeOverTemp()); 73 | telemetry.addData("M3 H-bridge over temp", motor3.isBridgeOverTemp()); 74 | 75 | telemetry.update(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /examples/src/main/java/org/openftc/revextensions2/examples/VoltageMonitorsExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | package org.openftc.revextensions2.examples; 23 | 24 | import com.qualcomm.robotcore.eventloop.opmode.OpMode; 25 | import com.qualcomm.robotcore.eventloop.opmode.TeleOp; 26 | 27 | import org.openftc.revextensions2.ExpansionHubEx; 28 | 29 | @TeleOp(group = "RevExtensions2Examples") 30 | public class VoltageMonitorsExample extends OpMode 31 | { 32 | ExpansionHubEx expansionHub; 33 | 34 | @Override 35 | public void init() 36 | { 37 | /* 38 | * Before init() was called on this user code, REV Extensions 2 39 | * was notified via OpModeManagerNotifier.Notifications and 40 | * it automatically took care of initializing the new objects 41 | * in the hardwaremap for you. Historically, you would have 42 | * needed to call RevExtensions2.init() 43 | */ 44 | expansionHub = hardwareMap.get(ExpansionHubEx.class, "Expansion Hub 2"); 45 | } 46 | 47 | @Override 48 | public void loop() 49 | { 50 | /* 51 | * ------------------------------------------------------------------------------------------------ 52 | * Voltage monitors 53 | * ------------------------------------------------------------------------------------------------ 54 | */ 55 | 56 | String header = 57 | "**********************************\n" + 58 | "VOLTAGE MONITORS EXAMPLE \n" + 59 | "**********************************\n"; 60 | telemetry.addLine(header); 61 | 62 | telemetry.addData("5v monitor", expansionHub.read5vMonitor(ExpansionHubEx.VoltageUnits.VOLTS)); //Voltage from the phone 63 | telemetry.addData("12v monitor", expansionHub.read12vMonitor(ExpansionHubEx.VoltageUnits.VOLTS)); //Battery voltage 64 | telemetry.update(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFTC/RevExtensions2/e98d1a55e58353d72985f1871c01adcc0781dfbb/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jun 02 15:16:59 EDT 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | Hello world. 2 | -------------------------------------------------------------------------------- /release-aar.gradle: -------------------------------------------------------------------------------- 1 | // ./gradlew clean build generateRelease 2 | apply plugin: 'maven' 3 | 4 | def groupId = project.PUBLISH_GROUP_ID 5 | def artifactId = project.PUBLISH_ARTIFACT_ID 6 | def version = project.PUBLISH_VERSION 7 | 8 | def localReleaseDest = "${buildDir}/release/${version}" 9 | 10 | task androidJavadocs(type: Javadoc) { 11 | failOnError = false 12 | source = android.sourceSets.main.java.srcDirs 13 | ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar" 14 | classpath += files(ext.androidJar) 15 | } 16 | 17 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 18 | classifier = 'javadoc' 19 | from androidJavadocs.destinationDir 20 | } 21 | 22 | task androidSourcesJar(type: Jar) { 23 | classifier = 'sources' 24 | from android.sourceSets.main.java.srcDirs 25 | } 26 | 27 | uploadArchives { 28 | repositories.mavenDeployer { 29 | pom.groupId = groupId 30 | pom.artifactId = artifactId 31 | pom.version = version 32 | // Add other pom properties here if you want (developer details / licenses) 33 | repository(url: "file://${localReleaseDest}") 34 | } 35 | } 36 | 37 | task zipRelease(type: Zip) { 38 | from localReleaseDest 39 | destinationDir buildDir 40 | archiveName "release-${version}.zip" 41 | } 42 | 43 | task generateRelease { 44 | doLast { 45 | println "Release ${version} can be found at ${localReleaseDest}/" 46 | println "Release ${version} zipped can be found ${buildDir}/release-${version}.zip" 47 | } 48 | } 49 | 50 | generateRelease.dependsOn(uploadArchives) 51 | generateRelease.dependsOn(zipRelease) 52 | 53 | 54 | artifacts { 55 | archives androidSourcesJar 56 | archives androidJavadocsJar 57 | } 58 | 59 | -------------------------------------------------------------------------------- /rev-extensions-2/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /rev-extensions-2/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | ext { 4 | PUBLISH_GROUP_ID = 'org.openftc' 5 | PUBLISH_ARTIFACT_ID = 'rev-extensions-2' 6 | PUBLISH_VERSION = '1.2' 7 | } 8 | 9 | android { 10 | compileSdkVersion 26 11 | 12 | defaultConfig { 13 | minSdkVersion 19 14 | targetSdkVersion 26 15 | versionCode 2 16 | versionName "1.2" 17 | 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | 27 | lintOptions { 28 | abortOnError false 29 | } 30 | } 31 | 32 | dependencies { 33 | //compileOnly fileTree(include: ['*.aar'], dir: '../libs') 34 | compileOnly 'org.openftc:hardware-stock:5.0' 35 | compileOnly 'org.openftc:robotcore-stock:5.0' 36 | } 37 | 38 | apply from: '../release-aar.gradle' -------------------------------------------------------------------------------- /rev-extensions-2/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /rev-extensions-2/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /rev-extensions-2/src/main/java/org/openftc/revextensions2/ExpansionHubEx.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | package org.openftc.revextensions2; 23 | 24 | import android.graphics.Color; 25 | 26 | import com.qualcomm.hardware.lynx.LynxCommExceptionHandler; 27 | import com.qualcomm.hardware.lynx.LynxModule; 28 | import com.qualcomm.hardware.lynx.LynxNackException; 29 | import com.qualcomm.hardware.lynx.commands.core.LynxGetADCCommand; 30 | import com.qualcomm.hardware.lynx.commands.core.LynxGetADCResponse; 31 | import com.qualcomm.hardware.lynx.commands.core.LynxGetBulkInputDataCommand; 32 | import com.qualcomm.hardware.lynx.commands.core.LynxGetBulkInputDataResponse; 33 | import com.qualcomm.hardware.lynx.commands.core.LynxI2cConfigureChannelCommand; 34 | import com.qualcomm.hardware.lynx.commands.core.LynxPhoneChargeControlCommand; 35 | import com.qualcomm.hardware.lynx.commands.core.LynxPhoneChargeQueryCommand; 36 | import com.qualcomm.hardware.lynx.commands.core.LynxPhoneChargeQueryResponse; 37 | import com.qualcomm.hardware.lynx.commands.core.LynxSetServoPulseWidthCommand; 38 | import com.qualcomm.hardware.lynx.commands.standard.LynxGetModuleStatusCommand; 39 | import com.qualcomm.hardware.lynx.commands.standard.LynxGetModuleStatusResponse; 40 | import com.qualcomm.hardware.lynx.commands.standard.LynxSetModuleLEDColorCommand; 41 | import com.qualcomm.robotcore.hardware.HardwareDevice; 42 | import com.qualcomm.robotcore.hardware.configuration.LynxConstants; 43 | 44 | import org.firstinspires.ftc.robotcore.internal.system.AppUtil; 45 | 46 | import java.util.regex.Matcher; 47 | import java.util.regex.Pattern; 48 | 49 | /** 50 | * Provides an implementation of the REV Expansion Hub's features that are not 51 | * included in the standard FTC SDK. 52 | */ 53 | public class ExpansionHubEx extends LynxCommExceptionHandler implements HardwareDevice 54 | { 55 | private LynxModule expansionHub; 56 | 57 | /* 58 | * Don't use this constructor in user-code; this object will be 59 | * hotswapped into the hardwareMap at runtime. 60 | */ 61 | ExpansionHubEx(LynxModule expansionHub) 62 | { 63 | this.expansionHub = expansionHub; 64 | } 65 | 66 | public LynxModule getStandardModule() 67 | { 68 | return expansionHub; 69 | } 70 | 71 | /*** 72 | * Set the color of the Expansion Hub's RGB status LED 73 | * 74 | * @param r red value 75 | * @param g green value 76 | * @param b blue value 77 | */ 78 | public synchronized void setLedColor(int r, int g, int b) 79 | { 80 | if(r > 255 || g > 255 || b > 255) 81 | { 82 | throw new IllegalArgumentException(); 83 | } 84 | 85 | setLedColor((byte)r, (byte)g, (byte)b); 86 | } 87 | 88 | /*** 89 | * Set the color of the Expansion Hub's RGB status LED 90 | * 91 | * @param r red value 92 | * @param g green value 93 | * @param b blue value 94 | */ 95 | public synchronized void setLedColor(byte r, byte g, byte b) 96 | { 97 | LynxSetModuleLEDColorCommand colorCommand = new LynxSetModuleLEDColorCommand(expansionHub, r, g, b); 98 | try 99 | { 100 | colorCommand.send(); 101 | } 102 | catch (InterruptedException | LynxNackException e) 103 | { 104 | handleException(e); 105 | } 106 | } 107 | 108 | /*** 109 | * Set the color of the Expansion Hub's RGB status LED 110 | * 111 | * @param resId the resId of a color you have defined in your colors.xml file 112 | */ 113 | public synchronized void setLedColor(int resId) 114 | { 115 | int color = AppUtil.getInstance().getRootActivity().getResources().getColor(resId); 116 | 117 | byte red = (byte) Color.red(color); 118 | byte green = (byte) Color.green(color); 119 | byte blue = (byte) Color.blue(color); 120 | 121 | setLedColor(red, green, blue); 122 | } 123 | 124 | public enum I2cBusSpeed 125 | { 126 | STANDARD_100K, 127 | FAST_400K, 128 | FASTPLUS_1M, 129 | HIGH_3_4M 130 | } 131 | 132 | /*** 133 | * Set the speed of all of the I2C buses on the Expansion Hub 134 | * in one go! 135 | * 136 | * @param speed the speed to set all of the buses to 137 | */ 138 | public synchronized void setAllI2cBusSpeeds(I2cBusSpeed speed) 139 | { 140 | for(int i = 0; i < LynxConstants.NUMBER_OF_I2C_BUSSES; i++) 141 | { 142 | setI2cBusSpeed(i, speed); 143 | } 144 | } 145 | 146 | /*** 147 | * Set the speed of an individual I2C bus on the Expansion Hub 148 | * 149 | * @param bus the bus to set the speed of (0...3) 150 | * @param speed the speed to set the bus to 151 | */ 152 | public synchronized void setI2cBusSpeed(int bus, I2cBusSpeed speed) 153 | { 154 | LynxI2cConfigureChannelCommand.SpeedCode speedCode = LynxI2cConfigureChannelCommand.SpeedCode.STANDARD_100K; 155 | 156 | switch (speed) 157 | { 158 | case STANDARD_100K: 159 | speedCode = LynxI2cConfigureChannelCommand.SpeedCode.STANDARD_100K; 160 | break; 161 | 162 | case FAST_400K: 163 | speedCode = LynxI2cConfigureChannelCommand.SpeedCode.FAST_400K; 164 | break; 165 | 166 | case FASTPLUS_1M: 167 | speedCode = LynxI2cConfigureChannelCommand.SpeedCode.FASTPLUS_1M; 168 | break; 169 | 170 | case HIGH_3_4M: 171 | speedCode = LynxI2cConfigureChannelCommand.SpeedCode.HIGH_3_4M; 172 | break; 173 | } 174 | 175 | LynxI2cConfigureChannelCommand command = new LynxI2cConfigureChannelCommand(expansionHub, bus, speedCode); 176 | try 177 | { 178 | command.send(); 179 | } 180 | catch (LynxNackException | InterruptedException e) 181 | { 182 | handleException(e); 183 | } 184 | } 185 | 186 | public enum CurrentDrawUnits 187 | { 188 | MILLIAMPS, 189 | AMPS 190 | } 191 | 192 | /** 193 | * Get the total current draw for the entire Expansion Hub. 194 | * 195 | * @param units the units to return the current draw in 196 | * @return Current draw for the entire Expansion Hub 197 | */ 198 | public synchronized double getTotalModuleCurrentDraw(CurrentDrawUnits units) 199 | { 200 | LynxGetADCCommand command = new LynxGetADCCommand(expansionHub, LynxGetADCCommand.Channel.BATTERY_CURRENT, LynxGetADCCommand.Mode.ENGINEERING); 201 | try 202 | { 203 | LynxGetADCResponse response = command.sendReceive(); 204 | 205 | int ma = response.getValue(); 206 | 207 | if(units == CurrentDrawUnits.MILLIAMPS) 208 | { 209 | return ma; 210 | } 211 | else if(units == CurrentDrawUnits.AMPS) 212 | { 213 | return ma/1000d; 214 | } 215 | } 216 | catch (InterruptedException | RuntimeException | LynxNackException e) 217 | { 218 | handleException(e); 219 | } 220 | return 0; 221 | } 222 | 223 | /** 224 | * Get the current draw for the servo bus. 225 | * 226 | * @param units the units to return the current draw in 227 | * @return Current draw of the servo bus 228 | * @deprecated This feature currently does not work (likely a bug in the Expansion Hub firmware). 229 | */ 230 | @Deprecated 231 | public synchronized double getServoBusCurrentDraw(CurrentDrawUnits units) 232 | { 233 | LynxGetADCCommand.Channel channel = LynxGetADCCommand.Channel.SERVO_CURRENT; 234 | 235 | LynxGetADCCommand command = new LynxGetADCCommand(expansionHub, channel, LynxGetADCCommand.Mode.ENGINEERING); 236 | try 237 | { 238 | LynxGetADCResponse response = command.sendReceive(); 239 | 240 | int ma = response.getValue(); 241 | 242 | if(units == CurrentDrawUnits.MILLIAMPS) 243 | { 244 | return ma; 245 | } 246 | else if(units == CurrentDrawUnits.AMPS) 247 | { 248 | return ma/1000d; 249 | } 250 | } 251 | catch (InterruptedException | RuntimeException | LynxNackException e) 252 | { 253 | handleException(e); 254 | } 255 | return 0; 256 | } 257 | 258 | /*** 259 | * Get the total current draw for the GPIO bus 260 | * 261 | * @param units the units to return the current draw in 262 | * @return current draw of the GPIO bus 263 | */ 264 | public synchronized double getGpioBusCurrentDraw(CurrentDrawUnits units) 265 | { 266 | LynxGetADCCommand.Channel channel = LynxGetADCCommand.Channel.GPIO_CURRENT; 267 | 268 | LynxGetADCCommand command = new LynxGetADCCommand(expansionHub, channel, LynxGetADCCommand.Mode.ENGINEERING); 269 | try 270 | { 271 | LynxGetADCResponse response = command.sendReceive(); 272 | 273 | int ma = response.getValue(); 274 | 275 | if(units == CurrentDrawUnits.MILLIAMPS) 276 | { 277 | return ma; 278 | } 279 | else if(units == CurrentDrawUnits.AMPS) 280 | { 281 | return ma/1000d; 282 | } 283 | } 284 | catch (InterruptedException | RuntimeException | LynxNackException e) 285 | { 286 | handleException(e); 287 | } 288 | return 0; 289 | } 290 | 291 | /*** 292 | * Get the total current draw for the I2C bus 293 | * 294 | * @param units the units to return the current draw in 295 | * @return the current draw of the I2C bus 296 | */ 297 | public synchronized double getI2cBusCurrentDraw(CurrentDrawUnits units) 298 | { 299 | LynxGetADCCommand.Channel channel = LynxGetADCCommand.Channel.I2C_BUS_CURRENT; 300 | 301 | LynxGetADCCommand command = new LynxGetADCCommand(expansionHub, channel, LynxGetADCCommand.Mode.ENGINEERING); 302 | try 303 | { 304 | LynxGetADCResponse response = command.sendReceive(); 305 | 306 | int ma = response.getValue(); 307 | 308 | if(units == CurrentDrawUnits.MILLIAMPS) 309 | { 310 | return ma; 311 | } 312 | else if(units == CurrentDrawUnits.AMPS) 313 | { 314 | return ma/1000d; 315 | } 316 | } 317 | catch (InterruptedException | RuntimeException | LynxNackException e) 318 | { 319 | handleException(e); 320 | } 321 | return 0; 322 | } 323 | 324 | public enum VoltageUnits 325 | { 326 | MILLIVOLTS, 327 | VOLTS 328 | } 329 | 330 | /*** 331 | * Get the voltage reported by the Hub's 5v rail monitor 332 | * 333 | * @param units the units to return the voltage in 334 | * @return the voltage reported by the Hub's 5v monitor 335 | */ 336 | public synchronized double read5vMonitor(VoltageUnits units) 337 | { 338 | LynxGetADCCommand.Channel channel = LynxGetADCCommand.Channel.FIVE_VOLT_MONITOR; 339 | 340 | LynxGetADCCommand command = new LynxGetADCCommand(expansionHub, channel, LynxGetADCCommand.Mode.ENGINEERING); 341 | try 342 | { 343 | LynxGetADCResponse response = command.sendReceive(); 344 | 345 | int mv = response.getValue(); 346 | 347 | if(units == VoltageUnits.MILLIVOLTS) 348 | { 349 | return mv; 350 | } 351 | else if(units == VoltageUnits.VOLTS) 352 | { 353 | return mv/1000d; 354 | } 355 | } 356 | catch (InterruptedException | RuntimeException | LynxNackException e) 357 | { 358 | handleException(e); 359 | } 360 | return 0; 361 | } 362 | 363 | /*** 364 | * Get the voltage reported by the Hub's 12v rail monitor 365 | * 366 | * @param units the units to return the voltage in 367 | * @return the voltage reported by the Hub's 12v monitor 368 | */ 369 | public synchronized double read12vMonitor(VoltageUnits units) 370 | { 371 | LynxGetADCCommand.Channel channel = LynxGetADCCommand.Channel.BATTERY_MONITOR; 372 | 373 | LynxGetADCCommand command = new LynxGetADCCommand(expansionHub, channel, LynxGetADCCommand.Mode.ENGINEERING); 374 | try 375 | { 376 | LynxGetADCResponse response = command.sendReceive(); 377 | 378 | int mv = response.getValue(); 379 | 380 | if(units == VoltageUnits.MILLIVOLTS) 381 | { 382 | return mv; 383 | } 384 | else if(units == VoltageUnits.VOLTS) 385 | { 386 | return mv/1000d; 387 | } 388 | } 389 | catch (InterruptedException | RuntimeException | LynxNackException e) 390 | { 391 | handleException(e); 392 | } 393 | return 0; 394 | } 395 | 396 | public enum TemperatureUnits 397 | { 398 | CELSIUS, 399 | FAHRENHEIT 400 | } 401 | 402 | /*** 403 | * Get the temperature reported by the Hub's internal temperature sensor 404 | * 405 | * @param units the units to return the temperature in 406 | * @return the Hub's internal temperature 407 | */ 408 | public synchronized double getInternalTemperature(TemperatureUnits units) 409 | { 410 | LynxGetADCCommand.Channel channel = LynxGetADCCommand.Channel.CONTROLLER_TEMPERATURE; 411 | 412 | LynxGetADCCommand command = new LynxGetADCCommand(expansionHub, channel, LynxGetADCCommand.Mode.ENGINEERING); 413 | try 414 | { 415 | LynxGetADCResponse response = command.sendReceive(); 416 | 417 | int degC = response.getValue(); 418 | 419 | if(units == TemperatureUnits.CELSIUS) 420 | { 421 | return degC; 422 | } 423 | else if(units == TemperatureUnits.FAHRENHEIT) 424 | { 425 | return (degC*(9d/5d)) + 32; 426 | } 427 | } 428 | catch (InterruptedException | RuntimeException | LynxNackException e) 429 | { 430 | handleException(e); 431 | } 432 | return 0; 433 | } 434 | 435 | /*** 436 | * Query if the Lynx Module is in an over-temp condition 437 | * 438 | * @return boolean indicating if the module is over-temp 439 | */ 440 | public synchronized boolean isModuleOverTemp() 441 | { 442 | LynxGetModuleStatusCommand command = new LynxGetModuleStatusCommand(expansionHub); 443 | 444 | try 445 | { 446 | LynxGetModuleStatusResponse response = command.sendReceive(); 447 | return response.isControllerOverTemp(); 448 | } 449 | 450 | catch (Exception e) 451 | { 452 | handleException(e); 453 | } 454 | 455 | return false; 456 | } 457 | 458 | /*** 459 | * Query if the Lynx Module has the phone charge feature enabled 460 | * @deprecated because this seems to lock up the Hub internally and crash the SDK 461 | * 462 | * @return boolean indicating if the module has the phone charge feature enabled 463 | */ 464 | @Deprecated 465 | public synchronized boolean isPhoneChargeEnabled() 466 | { 467 | LynxPhoneChargeQueryCommand command = new LynxPhoneChargeQueryCommand(expansionHub); 468 | 469 | try 470 | { 471 | LynxPhoneChargeQueryResponse response = command.sendReceive(); 472 | return response.isChargeEnabled(); 473 | } 474 | 475 | catch (Exception e) 476 | { 477 | handleException(e); 478 | } 479 | 480 | return false; 481 | } 482 | 483 | /*** 484 | * Control whether or not the Lynx Module's phone charging function is enabled 485 | * 486 | * @param chargeEnabled true for enabled, false for disabled 487 | */ 488 | public synchronized void setPhoneChargeEnabled(boolean chargeEnabled) 489 | { 490 | LynxPhoneChargeControlCommand controlCommand = new LynxPhoneChargeControlCommand(expansionHub, chargeEnabled); 491 | try 492 | { 493 | controlCommand.send(); 494 | } 495 | catch (InterruptedException | LynxNackException e) 496 | { 497 | handleException(e); 498 | } 499 | } 500 | 501 | /*** 502 | * Set the pulse width output of a servo port directly, rather than using 503 | * the -1 to 1 range from the SDK 504 | * 505 | * @param port the servo port to set the pulse width of 506 | * @param uS the pulse width (in uS) to set the above servo port to 507 | */ 508 | public synchronized void setServoPulseWidth(int port, int uS) 509 | { 510 | LynxSetServoPulseWidthCommand command = new LynxSetServoPulseWidthCommand(expansionHub, port, uS); 511 | 512 | try 513 | { 514 | command.send(); 515 | } 516 | catch (InterruptedException | LynxNackException e) 517 | { 518 | handleException(e); 519 | } 520 | } 521 | 522 | /*** 523 | * Grab a bunch of useful data from the Lynx module in one go 524 | * 525 | * @return an object that contains a bunch of useful data 526 | */ 527 | public synchronized RevBulkData getBulkInputData() 528 | { 529 | LynxGetBulkInputDataCommand command = new LynxGetBulkInputDataCommand(expansionHub); 530 | 531 | try 532 | { 533 | LynxGetBulkInputDataResponse response = command.sendReceive(); 534 | return new RevBulkData(response, expansionHub); 535 | } 536 | 537 | catch (Exception e) 538 | { 539 | handleException(e); 540 | } 541 | 542 | return null; 543 | } 544 | 545 | /** 546 | * Get the amount of current that a given motor is pulling from its H-bridge 547 | * 548 | * @param units the units to return the current draw in 549 | * @param port the port of the motor in question 550 | * @return the current draw in milliamps 551 | */ 552 | public synchronized double getMotorCurrentDraw(CurrentDrawUnits units, int port) 553 | { 554 | LynxConstants.validateMotorZ(port); 555 | 556 | LynxGetADCCommand.Channel channel = null; 557 | 558 | if (port == 0) 559 | { 560 | channel = LynxGetADCCommand.Channel.MOTOR0_CURRENT; 561 | } 562 | else if (port == 1) 563 | { 564 | channel = LynxGetADCCommand.Channel.MOTOR1_CURRENT; 565 | } 566 | else if (port == 2) 567 | { 568 | channel = LynxGetADCCommand.Channel.MOTOR2_CURRENT; 569 | } 570 | else if (port == 3) 571 | { 572 | channel = LynxGetADCCommand.Channel.MOTOR3_CURRENT; 573 | } 574 | 575 | LynxGetADCCommand command = new LynxGetADCCommand(expansionHub, channel, LynxGetADCCommand.Mode.ENGINEERING); 576 | 577 | try 578 | { 579 | LynxGetADCResponse response = command.sendReceive(); 580 | 581 | int ma = response.getValue(); 582 | 583 | if(units == CurrentDrawUnits.MILLIAMPS) 584 | { 585 | return ma; 586 | } 587 | else if(units == CurrentDrawUnits.AMPS) 588 | { 589 | return ma/1000d; 590 | } 591 | } 592 | catch (InterruptedException | RuntimeException | LynxNackException e) 593 | { 594 | handleException(e); 595 | } 596 | 597 | return 0; 598 | } 599 | 600 | /*** 601 | * Query as to whether the H-bridge for a given motor port is over-temp 602 | * 603 | * @param port the motor port in question 604 | * @return boolean indicating the H-bridge is over-temp 605 | */ 606 | public synchronized boolean isMotorBridgeOverTemp(int port) 607 | { 608 | LynxGetModuleStatusCommand command = new LynxGetModuleStatusCommand(expansionHub); 609 | 610 | try 611 | { 612 | LynxGetModuleStatusResponse response = command.sendReceive(); 613 | return response.isMotorBridgeOverTemp(port); 614 | } 615 | 616 | catch (Exception e) 617 | { 618 | handleException(e); 619 | } 620 | 621 | return false; 622 | } 623 | 624 | /*** 625 | * Query as to whether a given motor has lost (encoder?) counts 626 | * @deprecated because I have no idea what this actually does 627 | * 628 | * @param port the port of the motor in question 629 | * @return boolean indicating whether the motor has lost (encoder?) counts 630 | */ 631 | @Deprecated 632 | public synchronized boolean hasMotorLostCounts(int port) 633 | { 634 | LynxGetModuleStatusCommand command = new LynxGetModuleStatusCommand(expansionHub); 635 | 636 | try 637 | { 638 | LynxGetModuleStatusResponse response = command.sendReceive(); 639 | return response.hasMotorLostCounts(port); 640 | } 641 | 642 | catch (Exception e) 643 | { 644 | e.printStackTrace(); 645 | } 646 | 647 | return false; 648 | } 649 | 650 | /*** 651 | * Query as to which firmware this Expansion Hub is running 652 | * 653 | * @return firmware version string, in th format of maj.min.eng 654 | */ 655 | public synchronized String getFirmwareVersion() 656 | { 657 | //HW: 20, Maj: 1, Min: 7, Eng: 0 658 | 659 | String inputstring = expansionHub.getFirmwareVersionString(); 660 | 661 | if(inputstring.equals("unknown firmware") || inputstring.equals("firmware version unavailable")) 662 | { 663 | return inputstring; 664 | } 665 | 666 | int majVer = regexField("Maj", inputstring); 667 | int minVer = regexField("Min", inputstring); 668 | int engVer = regexField("Eng", inputstring); 669 | 670 | return String.format("%s.%s.%s", majVer, minVer, engVer); 671 | } 672 | 673 | /*** 674 | * Ask the Expansion Hub which hardware revision it is 675 | * 676 | * @return the hardware revision 677 | */ 678 | public synchronized int getHardwareRevision() 679 | { 680 | //HW: 20, Maj: 1, Min: 7, Eng: 0 681 | 682 | String inputstring = expansionHub.getFirmwareVersionString(); 683 | 684 | if(inputstring.equals("unknown firmware") || inputstring.equals("firmware version unavailable")) 685 | { 686 | return -1; 687 | } 688 | 689 | return regexField("HW", inputstring); 690 | } 691 | 692 | private int regexField(String thing, String input) 693 | { 694 | Pattern pattern = Pattern.compile(thing + ":\\ \\d+"); 695 | Matcher matcher = pattern.matcher(input); 696 | matcher.find(); 697 | 698 | String string = matcher.group(); 699 | 700 | return regexNum(string); 701 | } 702 | 703 | private int regexNum(String str) 704 | { 705 | Pattern pattern = Pattern.compile("\\d+"); 706 | Matcher matcher = pattern.matcher(str); 707 | matcher.find(); 708 | 709 | return Integer.parseInt(matcher.group()); 710 | } 711 | 712 | //----------------------------------------------------------------------------------------- 713 | // Ignore the following methods.... 714 | // 715 | // They're only here because in order to have this object hot-injected into the hardwareMap, 716 | // it needs to implement HardwareDevice. And well, in order to implement HardwareDevice, we 717 | // need to implement these methods. Sooo yeah... 718 | //----------------------------------------------------------------------------------------- 719 | 720 | @Override 721 | public Manufacturer getManufacturer() 722 | { 723 | return expansionHub.getManufacturer(); 724 | } 725 | 726 | @Override 727 | public String getDeviceName() 728 | { 729 | return expansionHub.getDeviceName(); 730 | } 731 | 732 | @Override 733 | public String getConnectionInfo() 734 | { 735 | return expansionHub.getConnectionInfo(); 736 | } 737 | 738 | @Override 739 | public int getVersion() 740 | { 741 | return expansionHub.getVersion(); 742 | } 743 | 744 | @Override 745 | public void resetDeviceConfigurationForOpMode() 746 | { 747 | expansionHub.resetDeviceConfigurationForOpMode(); 748 | } 749 | 750 | @Override 751 | public void close() 752 | { 753 | expansionHub.close(); 754 | } 755 | } -------------------------------------------------------------------------------- /rev-extensions-2/src/main/java/org/openftc/revextensions2/ExpansionHubMotor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | package org.openftc.revextensions2; 23 | 24 | import com.qualcomm.hardware.lynx.LynxController; 25 | import com.qualcomm.robotcore.hardware.DcMotor; 26 | import com.qualcomm.robotcore.hardware.DcMotorImplEx; 27 | import org.openftc.revextensions2.ExpansionHubEx.CurrentDrawUnits; 28 | 29 | /** 30 | * Extends a DcMotorImplEx to provide access to new features. 31 | * Note: motor MUST be attached to an Expansion Hub. 32 | */ 33 | public class ExpansionHubMotor extends DcMotorImplEx 34 | { 35 | private ExpansionHubEx expansionHubEx; 36 | 37 | /* 38 | * Don't use this constructor in user-code; this object will be 39 | * hotswapped into the hardwareMap at runtime. 40 | */ 41 | ExpansionHubMotor(DcMotor motor) 42 | { 43 | super(motor.getController(), motor.getPortNumber(), motor.getDirection(), motor.getMotorType()); 44 | expansionHubEx = new ExpansionHubEx(Utils.getLynxFromController((LynxController) motor.getController())); 45 | } 46 | 47 | /** 48 | * Get the amount of current this motor is pulling from its H-bridge 49 | * 50 | * @return the current draw in milliamps 51 | */ 52 | public double getCurrentDraw(CurrentDrawUnits units) 53 | { 54 | return expansionHubEx.getMotorCurrentDraw(units, getPortNumber()); 55 | } 56 | 57 | /*** 58 | * Query as to whether the H-bridge for this motor is over-temp 59 | * 60 | * @return boolean indicating the H-bridge is over-temp 61 | */ 62 | public boolean isBridgeOverTemp() 63 | { 64 | return expansionHubEx.isMotorBridgeOverTemp(getPortNumber()); 65 | } 66 | 67 | /*** 68 | * Query as to whether this motor has lost (encoder?) counts 69 | * @deprecated because I have no idea what this actually does 70 | * 71 | * @return boolean indicating whether the motor has lost (encoder?) counts 72 | */ 73 | @Deprecated 74 | public boolean hasLostCounts() 75 | { 76 | return expansionHubEx.hasMotorLostCounts(getPortNumber()); 77 | } 78 | } -------------------------------------------------------------------------------- /rev-extensions-2/src/main/java/org/openftc/revextensions2/ExpansionHubServo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | package org.openftc.revextensions2; 22 | 23 | import com.qualcomm.hardware.lynx.LynxController; 24 | import com.qualcomm.robotcore.hardware.Servo; 25 | import com.qualcomm.robotcore.hardware.ServoControllerEx; 26 | import com.qualcomm.robotcore.hardware.ServoImplEx; 27 | import com.qualcomm.robotcore.hardware.configuration.typecontainers.ServoConfigurationType; 28 | 29 | /** 30 | * Extends a ServoImplEx to provide access to new features. 31 | * Note: servo MUST be attached to an Expansion Hub. 32 | */ 33 | 34 | public class ExpansionHubServo extends ServoImplEx 35 | { 36 | private ExpansionHubEx expansionHubEx; 37 | 38 | /* 39 | * Don't use this constructor in user-code; this object will be 40 | * hotswapped into the hardwareMap at runtime. 41 | */ 42 | ExpansionHubServo(Servo servo) 43 | { 44 | super( 45 | (ServoControllerEx)servo.getController(), 46 | servo.getPortNumber(), 47 | servo.getDirection(), 48 | ServoConfigurationType.getStandardServoType()); 49 | 50 | expansionHubEx = new ExpansionHubEx(Utils.getLynxFromController((LynxController) servo.getController())); 51 | } 52 | 53 | /*** 54 | * Set the pulse width output of this servo port directly, rather than using 55 | * the -1 to 1 range from the SDK 56 | * 57 | * @param uS the pulse width (in uS) to set this servo port to 58 | */ 59 | public void setPulseWidthUs(int uS) 60 | { 61 | expansionHubEx.setServoPulseWidth(getPortNumber(), uS); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /rev-extensions-2/src/main/java/org/openftc/revextensions2/RE2Exception.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | package org.openftc.revextensions2; 23 | 24 | class RE2Exception extends RuntimeException 25 | { 26 | RE2Exception(){} 27 | 28 | RE2Exception(String msg) 29 | { 30 | super(msg); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rev-extensions-2/src/main/java/org/openftc/revextensions2/RevBulkData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | package org.openftc.revextensions2; 23 | 24 | import com.qualcomm.hardware.lynx.LynxAnalogInputController; 25 | import com.qualcomm.hardware.lynx.LynxController; 26 | import com.qualcomm.hardware.lynx.LynxDcMotorController; 27 | import com.qualcomm.hardware.lynx.LynxDigitalChannelController; 28 | import com.qualcomm.hardware.lynx.LynxModule; 29 | import com.qualcomm.hardware.lynx.commands.core.LynxGetBulkInputDataResponse; 30 | import com.qualcomm.robotcore.hardware.AnalogInput; 31 | import com.qualcomm.robotcore.hardware.AnalogInputController; 32 | import com.qualcomm.robotcore.hardware.DcMotor; 33 | import com.qualcomm.robotcore.hardware.DcMotorSimple; 34 | import com.qualcomm.robotcore.hardware.DigitalChannel; 35 | import com.qualcomm.robotcore.hardware.DigitalChannelController; 36 | import com.qualcomm.robotcore.hardware.DigitalChannelImpl; 37 | import com.qualcomm.robotcore.hardware.configuration.typecontainers.MotorConfigurationType; 38 | 39 | import org.firstinspires.ftc.robotcore.external.navigation.Rotation; 40 | 41 | import java.lang.reflect.Field; 42 | 43 | /** 44 | * This class is a wrapper over LynxGetBulkInputDataResponse that provides 45 | * easier-to-use methods to access the underlying data 46 | */ 47 | 48 | public class RevBulkData 49 | { 50 | private LynxGetBulkInputDataResponse response; 51 | private LynxModule module; 52 | 53 | /*** 54 | * Constructor 55 | * 56 | * @param response the raw response over which we are to be a wrapper 57 | * 58 | * @param module the Lynx module that this response came from; 59 | * used to verify that this response actually 60 | * contains the device information the user 61 | * was after. (They might have issued this command 62 | * to the wrong LynxModule) 63 | */ 64 | RevBulkData(LynxGetBulkInputDataResponse response, LynxModule module) 65 | { 66 | this.response = response; 67 | this.module = module; 68 | } 69 | 70 | // see DcMotorImpl#getOperationalDirection() 71 | private static DcMotorSimple.Direction getMotorOperationalDirection(DcMotor dcMotor) 72 | { 73 | MotorConfigurationType motorType = dcMotor.getMotorType(); 74 | DcMotorSimple.Direction direction = dcMotor.getDirection(); 75 | if (motorType.getOrientation() == Rotation.CCW) 76 | { 77 | return direction.inverted(); 78 | } 79 | else 80 | { 81 | return direction; 82 | } 83 | } 84 | 85 | //----------------------------------------------------------------------------------- 86 | // Encoder counts 87 | //----------------------------------------------------------------------------------- 88 | 89 | /*** 90 | * Get the encoder count of a motor port on the Expansion Hub 91 | * from which this bulk data response came. Please note that 92 | * the sign of the value will NOT be adjusted for whether or 93 | * not the motor was set to reverse mode; for that please use 94 | * the corresponding method that takes a DcMotor parameter 95 | * instead of the raw port number. 96 | * 97 | * @param motorNum the motor port for which to get the 98 | * encoder count [0-3] 99 | * 100 | * @return the encoder count (retrieved from this bulk packet) 101 | * of the motor port specified by the motorNum parameter 102 | */ 103 | public int getMotorCurrentPosition(int motorNum) 104 | { 105 | return response.getEncoder(motorNum); 106 | } 107 | 108 | /*** 109 | * Gets the encoder count for a motor that is connected to the 110 | * Expansion Hub from which this bulk data packet came. If the 111 | * user passes in a motor that is connected to a different Hub 112 | * than the one that this packet came from, an exception is thrown 113 | * Additionally, the sign of the value is adjusted to account for 114 | * whether the user set the motor direction to reverse. 115 | * 116 | * @param motor the motor for which to retrieve the encoder count 117 | * 118 | * @return the encoder count (retrieved from this bulk packet) for 119 | * the DcMotor object passed in 120 | */ 121 | public int getMotorCurrentPosition(DcMotor motor) 122 | { 123 | throwIfMotorInvalid(motor); 124 | int position = getMotorCurrentPosition(motor.getPortNumber()); 125 | if (getMotorOperationalDirection(motor) == DcMotorSimple.Direction.REVERSE) 126 | { 127 | return -position; 128 | } 129 | else 130 | { 131 | return position; 132 | } 133 | } 134 | 135 | //----------------------------------------------------------------------------------- 136 | // Encoder velocities 137 | //----------------------------------------------------------------------------------- 138 | 139 | /*** 140 | * Get the encoder velocity of a motor port on the Expansion Hub 141 | * from which this bulk data response came. Please note that 142 | * the sign of the value will NOT be adjusted for whether or 143 | * not the motor was set to reverse mode; for that please use 144 | * the corresponding method that takes a DcMotor parameter 145 | * instead of the raw port number. 146 | * 147 | * @param motorNum the motor port for which to get the 148 | * encoder velocity [0-3] 149 | * 150 | * @return the encoder velocity (retrieved from this bulk packet) 151 | * of the motor port specified by the motorNum parameter 152 | */ 153 | public int getMotorVelocity(int motorNum) 154 | { 155 | return response.getVelocity(motorNum); 156 | } 157 | 158 | /*** 159 | * Gets the encoder velocity for a motor that is connected to the 160 | * Expansion Hub from which this bulk data packet came. If the 161 | * user passes in a motor that is connected to a different Hub 162 | * than the one that this packet came from, an exception is thrown 163 | * Additionally, the sign of the value is adjusted to account for 164 | * whether the user set the motor direction to reverse. 165 | * 166 | * @param motor the motor for which to retrieve the encoder velocity 167 | * 168 | * @return the encoder velocity (retrieved from this bulk packet) for 169 | * the DcMotor object passed in 170 | */ 171 | public int getMotorVelocity(DcMotor motor) 172 | { 173 | throwIfMotorInvalid(motor); 174 | int velocity = getMotorVelocity(motor.getPortNumber()); 175 | if (getMotorOperationalDirection(motor) == DcMotorSimple.Direction.REVERSE) 176 | { 177 | return -velocity; 178 | } 179 | else 180 | { 181 | return velocity; 182 | } 183 | } 184 | 185 | //----------------------------------------------------------------------------------- 186 | // Position control at target position 187 | //----------------------------------------------------------------------------------- 188 | 189 | /*** 190 | * Checks whether a motor on a given port of the Expansion Hub 191 | * from which this bulk data response came is at it's target 192 | * encoder position 193 | * 194 | * @param motorNum the port for which to check whether the motor 195 | * is at it's target encoder position 196 | * 197 | * @return whether the motor in the specified port is at its target 198 | * encoder position as retrieved from this bulk packet 199 | */ 200 | public boolean isMotorAtTargetPosition(int motorNum) 201 | { 202 | return response.isAtTarget(motorNum); 203 | } 204 | 205 | /*** 206 | * Checks whether a given motor connected to the Expansion Hub 207 | * from which this bulk data response came is at it's target 208 | * encoder position. If the user passes in a motor that is connected 209 | * to a different Hub than the one that this packet came from, an 210 | * exception is thrown 211 | * 212 | * @param motor the motor for which to check if the target encoder 213 | * position has been reached 214 | * 215 | * @return whether the motor in the specified port is at its target 216 | * encoder position as retrieved from this bulk packet 217 | */ 218 | public boolean isMotorAtTargetPosition(DcMotor motor) 219 | { 220 | throwIfMotorInvalid(motor); 221 | return isMotorAtTargetPosition(motor.getPortNumber()); 222 | } 223 | 224 | //----------------------------------------------------------------------------------- 225 | // Analog I/Os 226 | //----------------------------------------------------------------------------------- 227 | 228 | /*** 229 | * Gets the ADC value of an analog pin on the Expansion Hub from 230 | * which this bulk data packet came. 231 | * 232 | * @param pin the analog pin for which to get the ADC value [0-3] 233 | * 234 | * @return the ADC value (retrieved from this bulk packet) for the 235 | * specified analog pin 236 | */ 237 | public int getAnalogInputValue(int pin) 238 | { 239 | return response.getAnalogInput(pin); 240 | } 241 | 242 | /*** 243 | * Gets the ADC value of an analog device that is connected to the 244 | * Expansion Hub from which this bulk data packet came. If the 245 | * user passes in a device that is connected to a different Hub 246 | * than the one that this packet came from, an exception is thrown 247 | * 248 | * @param input the analog device for which to get the ADC value 249 | * 250 | * @return the ADC value (retrieved from this bulk packet) for the 251 | * AnalogInput object passed in 252 | */ 253 | public int getAnalogInputValue(AnalogInput input) 254 | { 255 | AnalogInputController controller = null; 256 | int port = -1; 257 | 258 | try 259 | { 260 | Field controllerField = AnalogInput.class.getDeclaredField("controller"); 261 | controllerField.setAccessible(true); 262 | controller = (AnalogInputController) controllerField.get(input); 263 | 264 | Field channelField = AnalogInput.class.getDeclaredField("channel"); 265 | channelField.setAccessible(true); 266 | port = (int) channelField.get(input); 267 | } 268 | catch (Exception e) 269 | { 270 | throw new RE2Exception("Failed to reflect on AnalogInput! Please report this as an issue on the GitHub repository."); 271 | } 272 | 273 | if(!(controller instanceof LynxAnalogInputController)) 274 | { 275 | throw new RevBulkDataException(String.format("AnalogInput %s is not attached to a Lynx module!", Utils.getHwMapName(input))); 276 | } 277 | 278 | if(!validateLynxController((LynxController) controller)) 279 | { 280 | throw new RevBulkDataException(String.format("AnalogInput %s is attached to a different Lynx module than the one that this bulk command was issued to!", Utils.getHwMapName(input))); 281 | } 282 | 283 | return getAnalogInputValue(port); 284 | } 285 | 286 | //----------------------------------------------------------------------------------- 287 | // Digital I/Os 288 | //----------------------------------------------------------------------------------- 289 | 290 | /*** 291 | * Gets the state of a digital pin on the Expansion Hub from which 292 | * this bulk data packet came. 293 | * 294 | * @param pin the digital pin for which to get the state [0-7] 295 | * 296 | * @return the state (retrieved from this bulk packet) of the 297 | * specified digital pin 298 | */ 299 | public boolean getDigitalInputState(int pin) 300 | { 301 | return response.getDigitalInput(pin); 302 | } 303 | 304 | /*** 305 | * Gets the state of a digital device that is connected to the 306 | * Expansion Hub from which this bulk data packet came. If the 307 | * user passes in a device that is connected to a different Hub 308 | * than the one that this packet came from, an exception is thrown 309 | * 310 | * @param digitalChannel the digital device for which to get the state 311 | * 312 | * @return the state (retrieved from this bulk packet) for the 313 | * DigitalChannel object passed in 314 | */ 315 | public boolean getDigitalInputState(DigitalChannel digitalChannel) 316 | { 317 | DigitalChannelController controller = null; 318 | int port = -1; 319 | 320 | try 321 | { 322 | Field controllerField = DigitalChannelImpl.class.getDeclaredField("controller"); 323 | controllerField.setAccessible(true); 324 | controller = (DigitalChannelController) controllerField.get(digitalChannel); 325 | 326 | Field channelField = DigitalChannelImpl.class.getDeclaredField("channel"); 327 | channelField.setAccessible(true); 328 | port = (int) channelField.get(digitalChannel); 329 | } 330 | catch (Exception e) 331 | { 332 | throw new RE2Exception("Failed to reflect on DigitalChannelImpl! Please report this as an issue on the GitHub repository."); 333 | } 334 | 335 | if(!(controller instanceof LynxDigitalChannelController)) 336 | { 337 | throw new RevBulkDataException(String.format("DigitalChannel %s is not attached to a Lynx module!", Utils.getHwMapName(digitalChannel))); 338 | } 339 | 340 | if(!validateLynxController((LynxController) controller)) 341 | { 342 | throw new RevBulkDataException(String.format("DigitalChannel %s is attached to a different Lynx module than the one that this bulk command was issued to!", Utils.getHwMapName(digitalChannel))); 343 | } 344 | 345 | return getDigitalInputState(port); 346 | } 347 | 348 | //----------------------------------------------------------------------------------- 349 | // Utility functions 350 | //----------------------------------------------------------------------------------- 351 | 352 | /*** 353 | * Checks to see if the motor passed in is not attached to a Lynx module, 354 | * or if it is attached to a different Lynx module than the one that this 355 | * bulk packet came from. If either of those two conditions is true, an 356 | * exception is thrown. 357 | * 358 | * @param motor the motor to validate 359 | */ 360 | private void throwIfMotorInvalid(DcMotor motor) 361 | { 362 | // Is this motor attached to a Lynx module? 363 | if(!(motor.getController() instanceof LynxDcMotorController)) 364 | { 365 | throw new RevBulkDataException(String.format("Motor %s is not attached to a Lynx module!", Utils.getHwMapName(motor))); 366 | } 367 | 368 | // Is this motor attached to a different Lynx module than the one 369 | // that this packet came from? 370 | if(!validateLynxController((LynxController) motor.getController())) 371 | { 372 | throw new RevBulkDataException(String.format("Motor %s is attached to a different Lynx module than the one that this bulk command was issued to!", Utils.getHwMapName(motor))); 373 | } 374 | } 375 | 376 | /*** 377 | * Checks to see whether a Lynx controller is attached to the Lynx 378 | * module that this packet came from 379 | * 380 | * @param controller the controller to validate 381 | * 382 | * @return whether or not the controller is attached to the Lynx 383 | * module that this packet came from 384 | */ 385 | private boolean validateLynxController(LynxController controller) 386 | { 387 | return validateLynxModule(Utils.getLynxFromController(controller)); 388 | } 389 | 390 | /*** 391 | * Checks to see whether a Lynx module passed in is the same one 392 | * that this packet came from 393 | * 394 | * @param moduleToValidate the module to validate 395 | * 396 | * @return whether or not a Lynx module passed in is the same one 397 | * that this packet came from 398 | */ 399 | private boolean validateLynxModule(LynxModule moduleToValidate) 400 | { 401 | //TODO: can we just replace with an '=='? 402 | return moduleToValidate.getModuleAddress() == module.getModuleAddress(); 403 | } 404 | } 405 | -------------------------------------------------------------------------------- /rev-extensions-2/src/main/java/org/openftc/revextensions2/RevBulkDataException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | package org.openftc.revextensions2; 23 | 24 | class RevBulkDataException extends RE2Exception 25 | { 26 | RevBulkDataException(){} 27 | 28 | RevBulkDataException(String msg) 29 | { 30 | super(msg); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rev-extensions-2/src/main/java/org/openftc/revextensions2/RevExtensions2.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | package org.openftc.revextensions2; 23 | 24 | import android.content.Context; 25 | 26 | import com.qualcomm.robotcore.eventloop.opmode.AnnotatedOpModeManager; 27 | import com.qualcomm.robotcore.eventloop.opmode.OpMode; 28 | import com.qualcomm.robotcore.eventloop.opmode.OpModeManagerNotifier; 29 | import com.qualcomm.robotcore.eventloop.opmode.OpModeRegistrar; 30 | import com.qualcomm.robotcore.util.RobotLog; 31 | 32 | import org.firstinspires.ftc.robotcore.internal.opmode.OpModeManagerImpl; 33 | 34 | /*** 35 | * This class manages hotswapping of the hardwareMap 36 | */ 37 | 38 | class RevExtensions2 39 | { 40 | /* 41 | * NOTE: We cannot simply pass `new OpModeNotifications()` inline to the call 42 | * to register the listener, because the SDK stores the list of listeners in 43 | * a WeakReference set. This causes the object to be garbage collected because 44 | * nothing else is holding a reference to it. 45 | */ 46 | private static final OpModeNotifications opModeNotifications = new OpModeNotifications(); 47 | 48 | /* 49 | * By annotating this method with @OpModeRegistrar, it will be called 50 | * automatically by the SDK as it is scanning all the classes in the app 51 | * (for @Teleop, etc.) while it is "starting" the robot. 52 | */ 53 | @OpModeRegistrar 54 | public static void setupOpModeListenerOnStartRobot(Context context, AnnotatedOpModeManager manager) 55 | { 56 | /* 57 | * Because this is called every time the robot is "restarted", one 58 | * would think that we should have a boolean to check whether we've 59 | * already registered this listener to prevent registering duplicate 60 | * listeners. However, the OpModeManager that we register with 61 | * is a child of FtcEventLoop, and the EventLoop is re-created every 62 | * time the robot is "restarted". So thus, we do actually need to 63 | * register the listener every time this method is called. 64 | */ 65 | Utils.getOpModeManager().registerListener(opModeNotifications); 66 | } 67 | 68 | private static class OpModeNotifications implements OpModeManagerNotifier.Notifications 69 | { 70 | @Override 71 | public void onOpModePreInit(OpMode opMode) 72 | { 73 | /* 74 | * We only hotswap the hardware map if this is NOT the 75 | * "default" (STOP ROBOT) OpMode 76 | */ 77 | if(!(opMode instanceof OpModeManagerImpl.DefaultOpMode)) 78 | { 79 | RobotLog.dd("RevExtensions2", "Hotswapping hardware map"); 80 | Utils.hotswapHardwareMap(); 81 | } 82 | } 83 | 84 | @Override 85 | public void onOpModePreStart(OpMode opMode) 86 | { 87 | 88 | } 89 | 90 | @Override 91 | public void onOpModePostStop(OpMode opMode) 92 | { 93 | /* 94 | * We only deswap the hardware map if this is NOT the 95 | * "default" (STOP ROBOT) OpMode 96 | */ 97 | if(!(opMode instanceof OpModeManagerImpl.DefaultOpMode)) 98 | { 99 | RobotLog.dd("RevExtensions2", "Deswapping hardware map"); 100 | Utils.deswapHardwareMap(); 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /rev-extensions-2/src/main/java/org/openftc/revextensions2/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | package org.openftc.revextensions2; 23 | 24 | import com.qualcomm.hardware.lynx.LynxController; 25 | import com.qualcomm.hardware.lynx.LynxDcMotorController; 26 | import com.qualcomm.hardware.lynx.LynxModule; 27 | import com.qualcomm.hardware.lynx.LynxServoController; 28 | import com.qualcomm.robotcore.hardware.DcMotor; 29 | import com.qualcomm.robotcore.hardware.DcMotorEx; 30 | import com.qualcomm.robotcore.hardware.DcMotorImplEx; 31 | import com.qualcomm.robotcore.hardware.HardwareDevice; 32 | import com.qualcomm.robotcore.hardware.HardwareMap; 33 | import com.qualcomm.robotcore.hardware.Servo; 34 | import com.qualcomm.robotcore.hardware.ServoControllerEx; 35 | import com.qualcomm.robotcore.hardware.ServoImplEx; 36 | import com.qualcomm.robotcore.hardware.configuration.typecontainers.ServoConfigurationType; 37 | 38 | import org.firstinspires.ftc.robotcore.internal.opmode.OpModeManagerImpl; 39 | import org.firstinspires.ftc.robotcore.internal.system.AppUtil; 40 | 41 | import java.lang.reflect.Field; 42 | import java.util.ArrayList; 43 | import java.util.HashMap; 44 | import java.util.Map; 45 | 46 | /** 47 | * This class does Blackmagic 48 | */ 49 | 50 | class Utils 51 | { 52 | static OpModeManagerImpl getOpModeManager() 53 | { 54 | return OpModeManagerImpl.getOpModeManagerOfActivity(AppUtil.getInstance().getRootActivity()); 55 | } 56 | 57 | static HardwareMap getHardwareMap() 58 | { 59 | return getOpModeManager().getHardwareMap(); 60 | } 61 | 62 | static void hotswapHardwareMap() 63 | { 64 | HardwareMap map = getHardwareMap(); 65 | 66 | swapStandardMotorsForLynx(map); 67 | swapStandardServosForLynx(map); 68 | addExpansionHubExForEachLynxModule(map); 69 | } 70 | 71 | static void deswapHardwareMap() 72 | { 73 | HardwareMap map = getHardwareMap(); 74 | 75 | deswapLynxMotors(map); 76 | deswapLynxServos(map); 77 | removeExpansionHubExForEachLynxModule(map); 78 | } 79 | 80 | //------------------------------------------------------------------------------------------------------------------------------------------ 81 | // Swapping 82 | //------------------------------------------------------------------------------------------------------------------------------------------- 83 | 84 | private static void swapStandardMotorsForLynx(HardwareMap map) 85 | { 86 | //----------------------------------------------------------------------------------- 87 | // Motors: standard --> Lynx 88 | //----------------------------------------------------------------------------------- 89 | 90 | ArrayList> motorsToRecreateAsLynx = new ArrayList<>(); 91 | 92 | // TODO: Process both map.dcMotor and the root map. 93 | for (Map.Entry entry : map.dcMotor.entrySet()) 94 | { 95 | if (!(entry.getValue() instanceof ExpansionHubMotor) 96 | && entry.getValue() instanceof DcMotorEx 97 | && entry.getValue().getController() instanceof LynxDcMotorController) 98 | { 99 | motorsToRecreateAsLynx.add(entry); 100 | } 101 | } 102 | 103 | if (!motorsToRecreateAsLynx.isEmpty()) 104 | { 105 | for (Map.Entry entry : motorsToRecreateAsLynx) 106 | { 107 | map.dcMotor.remove(entry.getKey()); 108 | } 109 | 110 | for (Map.Entry entry : motorsToRecreateAsLynx) 111 | { 112 | map.dcMotor.put(entry.getKey(), new ExpansionHubMotor(entry.getValue())); 113 | } 114 | } 115 | } 116 | 117 | private static void swapStandardServosForLynx(HardwareMap map) 118 | { 119 | //----------------------------------------------------------------------------------- 120 | // Servos: standard --> Lynx 121 | //----------------------------------------------------------------------------------- 122 | 123 | ArrayList> servosToRecreateAsLynx = new ArrayList<>(); 124 | 125 | // TODO: Process both map.servo and the root map. 126 | for (Map.Entry entry : map.servo.entrySet()) 127 | { 128 | if (!(entry.getValue() instanceof ExpansionHubServo) 129 | && entry.getValue() instanceof ServoImplEx 130 | && entry.getValue().getController() instanceof LynxServoController) 131 | { 132 | servosToRecreateAsLynx.add(entry); 133 | } 134 | } 135 | 136 | if (!servosToRecreateAsLynx.isEmpty()) 137 | { 138 | for (Map.Entry entry : servosToRecreateAsLynx) 139 | { 140 | map.servo.remove(entry.getKey()); 141 | } 142 | 143 | for (Map.Entry entry : servosToRecreateAsLynx) 144 | { 145 | map.servo.put(entry.getKey(), new ExpansionHubServo(entry.getValue())); 146 | } 147 | } 148 | } 149 | 150 | private static void addExpansionHubExForEachLynxModule(HardwareMap map) 151 | { 152 | //----------------------------------------------------------------------------------- 153 | // LynxModules --+> ExpansionHubEx 154 | //----------------------------------------------------------------------------------- 155 | 156 | HashMap enhancedLynxModulesToInject = new HashMap<>(); 157 | 158 | for (LynxModule module : map.getAll(LynxModule.class)) 159 | { 160 | if (!hwMapContainsEnhancedModule(module)) 161 | { 162 | enhancedLynxModulesToInject.put(getHwMapName(module), new ExpansionHubEx(module)); 163 | } 164 | } 165 | 166 | for (Map.Entry entry : enhancedLynxModulesToInject.entrySet()) 167 | { 168 | map.put(entry.getKey(), entry.getValue()); 169 | } 170 | } 171 | 172 | //------------------------------------------------------------------------------------------------------------------------------------------ 173 | // Deswapping 174 | //------------------------------------------------------------------------------------------------------------------------------------------- 175 | 176 | private static void deswapLynxMotors(HardwareMap map) 177 | { 178 | //----------------------------------------------------------------------------------- 179 | // Motors: Lynx --> standard 180 | //----------------------------------------------------------------------------------- 181 | 182 | ArrayList> lynxMotorsToRecreateAsStandard = new ArrayList<>(); 183 | 184 | for (Map.Entry entry : map.dcMotor.entrySet()) 185 | { 186 | if (entry.getValue() instanceof ExpansionHubMotor) 187 | { 188 | lynxMotorsToRecreateAsStandard.add(entry); 189 | } 190 | } 191 | 192 | if (!lynxMotorsToRecreateAsStandard.isEmpty()) 193 | { 194 | for (Map.Entry entry : lynxMotorsToRecreateAsStandard) 195 | { 196 | map.dcMotor.remove(entry.getKey()); 197 | } 198 | 199 | for (Map.Entry entry : lynxMotorsToRecreateAsStandard) 200 | { 201 | map.dcMotor.put(entry.getKey(), 202 | new DcMotorImplEx( 203 | entry.getValue().getController(), 204 | entry.getValue().getPortNumber(), 205 | entry.getValue().getDirection(), 206 | entry.getValue().getMotorType())); 207 | } 208 | } 209 | 210 | } 211 | 212 | private static void deswapLynxServos(HardwareMap map) 213 | { 214 | //----------------------------------------------------------------------------------- 215 | // Servos: Lynx --> standard 216 | //----------------------------------------------------------------------------------- 217 | 218 | ArrayList> lynxServosToRecreateAsStandard = new ArrayList<>(); 219 | 220 | for (Map.Entry entry : map.servo.entrySet()) 221 | { 222 | if (entry.getValue() instanceof ExpansionHubServo) 223 | { 224 | lynxServosToRecreateAsStandard.add(entry); 225 | } 226 | } 227 | 228 | if (!lynxServosToRecreateAsStandard.isEmpty()) 229 | { 230 | for (Map.Entry entry : lynxServosToRecreateAsStandard) 231 | { 232 | map.servo.remove(entry.getKey()); 233 | } 234 | 235 | for (Map.Entry entry : lynxServosToRecreateAsStandard) 236 | { 237 | map.servo.put(entry.getKey(), 238 | new ServoImplEx( 239 | (ServoControllerEx) entry.getValue().getController(), 240 | entry.getValue().getPortNumber(), 241 | entry.getValue().getDirection(), 242 | ServoConfigurationType.getStandardServoType())); 243 | } 244 | } 245 | } 246 | 247 | private static void removeExpansionHubExForEachLynxModule(HardwareMap map) 248 | { 249 | //----------------------------------------------------------------------------------- 250 | // Remove ExpHbExs 251 | //----------------------------------------------------------------------------------- 252 | 253 | HashMap enhancedLynxModulesToRemove = new HashMap<>(); 254 | 255 | for (ExpansionHubEx module : map.getAll(ExpansionHubEx.class)) 256 | { 257 | enhancedLynxModulesToRemove.put(getHwMapName(module), module); 258 | } 259 | 260 | if (!enhancedLynxModulesToRemove.isEmpty()) 261 | { 262 | for (Map.Entry entry : enhancedLynxModulesToRemove.entrySet()) 263 | { 264 | map.remove(entry.getKey(), entry.getValue()); 265 | } 266 | } 267 | } 268 | 269 | //------------------------------------------------------------------------------------------------------------------------------------------ 270 | // Misc. 271 | //------------------------------------------------------------------------------------------------------------------------------------------- 272 | 273 | static String getHwMapName(HardwareDevice device) 274 | { 275 | return getHardwareMap().getNamesOf(device).iterator().next(); 276 | } 277 | 278 | private static boolean hwMapContainsEnhancedModule(LynxModule module) 279 | { 280 | for(ExpansionHubEx enhancedModule : getHardwareMap().getAll(ExpansionHubEx.class)) 281 | { 282 | //TODO: can we just replace with an '=='? 283 | if(module.getModuleAddress() == enhancedModule.getStandardModule().getModuleAddress()) 284 | { 285 | return true; 286 | } 287 | } 288 | 289 | return false; 290 | } 291 | 292 | static LynxModule getLynxFromController(LynxController controller) 293 | { 294 | /* 295 | * NOTE: Historically we invoked the getModule() method 296 | * rather than grabbing the field directly. However, this 297 | * was reported to be causing problems in the wild because 298 | * getModule() actually returns a "PretendLynxModule" if 299 | * its internal "isHooked" boolean is false (as can happen 300 | * during a disconnect). This caused the cast to LynxModule 301 | * to fail and crashed the user code with an RE2Exception. 302 | * 303 | * See https://github.com/OpenFTC/RevExtensions2/issues/6 304 | */ 305 | 306 | // We can get the underlying LynxModule object through a 307 | // LynxController object, but only through reflection. 308 | Field moduleField; 309 | 310 | try 311 | { 312 | // The "module" field is located within the LynxController class 313 | moduleField = LynxController.class.getDeclaredField("module"); 314 | 315 | // Ensures the field is accessible for the next line. We still catch 316 | // the (impossible) IllegalAccessException just to be safe. 317 | moduleField.setAccessible(true); 318 | 319 | // Actually get the value from the controller that was passed in. 320 | return (LynxModule) moduleField.get(controller); 321 | } 322 | catch (Exception e) 323 | { 324 | throw new RE2Exception("Failed to reflect on LynxController! Please report this as an issue on the GitHub repository."); 325 | } 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /rev-extensions-2/src/main/java/org/openftc/revextensions2/VexMC29.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 OpenFTC Team 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | package org.openftc.revextensions2; 22 | 23 | import com.qualcomm.robotcore.hardware.ControlSystem; 24 | import com.qualcomm.robotcore.hardware.configuration.ServoFlavor; 25 | import com.qualcomm.robotcore.hardware.configuration.annotations.DeviceProperties; 26 | import com.qualcomm.robotcore.hardware.configuration.annotations.ServoType; 27 | 28 | /** 29 | * Support for the VEX Motor Controller 29 30 | * 31 | * To use an MC29, connect it to a servo port, then on the Robot Controller or 32 | * Driver Station, configure the port you connected it to as a VEX Motor Controller 29 33 | * 34 | */ 35 | @ServoType(flavor = ServoFlavor.CONTINUOUS, usPulseLower = 1000, usPulseUpper = 2000) 36 | @DeviceProperties(xmlTag = "VexMC29", name = "VEX Motor Controller 29", description = "VEX Motor Controller 29", compatibleControlSystems = ControlSystem.REV_HUB) 37 | public interface VexMC29 38 | { 39 | } 40 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':rev-extensions-2', ':examples' 2 | --------------------------------------------------------------------------------