diff --git a/docs/requirements.txt b/docs/requirements.txt index 20bbd59f03..4a80bea4dd 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -36,7 +36,6 @@ sphinx-autobuild==2024.10.3 sphinx-basic-ng==1.0.0b2 sphinx-notfound-page==1.1.0 sphinx-rtd-theme==3.0.2 -sphinx-tabs==3.4.7 sphinx_design==0.6.1 sphinxcontrib-applehelp==2.0.0 sphinxcontrib-devhelp==2.0.0 diff --git a/docs/source/docs/advanced-installation/sw_install/files/Limelight2+/hardwareConfig.json b/docs/source/docs/advanced-installation/sw_install/files/Limelight2+/hardwareConfig.json index e43b1e7940..6d0b568756 100644 --- a/docs/source/docs/advanced-installation/sw_install/files/Limelight2+/hardwareConfig.json +++ b/docs/source/docs/advanced-installation/sw_install/files/Limelight2+/hardwareConfig.json @@ -3,5 +3,8 @@ "ledPins" : [ 13, 18 ], "ledsCanDim" : true, "ledPWMFrequency" : 1000, + "statusLEDType": "Green-Yellow", + "statusLEDPins": [ 5, 4 ], + "statusLEDActiveHigh": false, "vendorFOV" : 75.76079874010732 } diff --git a/docs/source/docs/advanced-installation/sw_install/files/Limelight2/hardwareConfig.json b/docs/source/docs/advanced-installation/sw_install/files/Limelight2/hardwareConfig.json index 33afd3cf4f..62f40f888e 100644 --- a/docs/source/docs/advanced-installation/sw_install/files/Limelight2/hardwareConfig.json +++ b/docs/source/docs/advanced-installation/sw_install/files/Limelight2/hardwareConfig.json @@ -2,5 +2,8 @@ "deviceName" : "Limelight 2", "ledPins" : [ 17, 18 ], "ledsCanDim" : false, + "statusLEDType": "Green-Yellow", + "statusLEDPins": [ 5, 4 ], + "statusLEDActiveHigh": false, "vendorFOV" : 75.76079874010732 } diff --git a/docs/source/docs/hardware/customhardware.md b/docs/source/docs/hardware/customhardware.md index b18c54e5ef..8e0f6449ae 100644 --- a/docs/source/docs/hardware/customhardware.md +++ b/docs/source/docs/hardware/customhardware.md @@ -19,14 +19,18 @@ When running on Linux, PhotonVision can use [diozero](https://www.diozero.com) t "ledsCanDim" : true, "ledBrightnessRange" : [ 0, 100 ], "ledPWMFrequency" : 0, - "statusRGBPins" : [ ], - "statusRGBActiveHigh" : false, + "statusLEDType": "RGB", + "statusLEDPins" : [ ], + "statusLEDActiveHigh" : false, } ``` -:::{note} -No hardware boards with status RGB LED pins or non-dimming LED's have been tested yet. Please reach out to the development team if these features are desired, they can assist with configuration and testing. -::: +There are currently two types of status LEDs supported: + +* `RGB` (default): A singular LED mixing separate red, green, and blue inputs +* `GY`/`Green-Yellow`: A pair of independent green and yellow LEDs + +For an explanation of the colors used for status LEDs, see {ref}`Status LEDs` ### GPIO Pinout @@ -134,8 +138,9 @@ Here is a complete example `hardwareConfig.json`: "ledsCanDim" : true, "ledBrightnessRange" : [ 0, 100 ], "ledPWMFrequency" : 0, - "statusRGBPins" : [ ], - "statusRGBActiveHigh" : false, + "statusLEDType": "RGB", + "statusLEDPins" : [ ], + "statusLEDActiveHigh" : false, "getGPIOCommand" : "getGPIO {p}", "setGPIOCommand" : "setGPIO {p} {s}", "setPWMCommand" : "setPWM {p} {v}", diff --git a/docs/source/docs/troubleshooting/images/led.svg b/docs/source/docs/troubleshooting/images/led.svg new file mode 100644 index 0000000000..ba34139089 --- /dev/null +++ b/docs/source/docs/troubleshooting/images/led.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/source/docs/troubleshooting/index.md b/docs/source/docs/troubleshooting/index.md index 00fe776352..64110cda0d 100644 --- a/docs/source/docs/troubleshooting/index.md +++ b/docs/source/docs/troubleshooting/index.md @@ -5,6 +5,7 @@ common-errors logging +status-leds camera-troubleshooting networking-troubleshooting unix-commands diff --git a/docs/source/docs/troubleshooting/status-leds.md b/docs/source/docs/troubleshooting/status-leds.md new file mode 100644 index 0000000000..58510dc5b0 --- /dev/null +++ b/docs/source/docs/troubleshooting/status-leds.md @@ -0,0 +1,92 @@ +--- +myst: + substitutions: + led_loader: | + ```{image} images/led.svg + :height: 0 + ``` + led_green: | + ```{raw} html + + + + ``` + led_solid_blue: | + ```{raw} html + + + + + ``` + led_yellow: | + ```{raw} html + + + + ``` + led_blue: | + ```{raw} html + + + + ``` + led_red: | + ```{raw} html + + + + ``` + led_off: | + ```{raw} html + + + + + ``` + led_fast_green: | + ```{raw} html + + + + + + ``` + led_solid_yellow: | + ```{raw} html + + + + + ``` +--- + +# Status LEDs + +PhotonVision has support for multiple kinds of status LEDs. Make sure you reference the correct table for the type present on your hardware. + +## RGB LED + + Color | Flashing | Preview | Status +--------|----------|:--------------------:|----------------------------------------------- + Green | Yes | {{ led_green }} | Running normally, no targets visible + Blue | No | {{ led_solid_blue }} | Running normally, targets visible + Yellow | Yes | {{ led_yellow }} | NT Disconnected, no targets visible + Blue | Yes | {{ led_blue }} | NT Disconnected, targets visible + Red | Yes | {{ led_red }} | Initializing or faulted, not running + Off | No | {{ led_off }} | No power or initialization fault, not running + +## Green and Yellow LEDs + +Used on Limelight 1, 2, 2+, 3, 3G, and 3A + +Green and Yellow LED patterns may be active at the same time + + Color | Pattern | Preview | Status +--------|----------------|:------------------------------------:|------------------------------------------------- + Green | Slow Flashing | {{ led_green }} {{ led_off }} | No targets visible + Green | Quick Flashing | {{ led_fast_green }} {{ led_off }} | Targets visible + Yellow | Flashing | {{ led_off }} {{ led_yellow }} | NT Disconnected + Yellow | Solid | {{ led_off }} {{ led_solid_yellow }} | NT Connected + Both | Off | {{ led_off }} {{ led_off }} | No power, initializing, or faulted, not running + +{{ led_loader }} diff --git a/photon-core/src/main/java/org/photonvision/common/configuration/HardwareConfig.java b/photon-core/src/main/java/org/photonvision/common/configuration/HardwareConfig.java index 387ba6bf3a..23d875bfa3 100644 --- a/photon-core/src/main/java/org/photonvision/common/configuration/HardwareConfig.java +++ b/photon-core/src/main/java/org/photonvision/common/configuration/HardwareConfig.java @@ -17,8 +17,10 @@ package org.photonvision.common.configuration; +import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import java.util.ArrayList; +import org.photonvision.common.hardware.statusLED.StatusLEDType; @JsonIgnoreProperties(ignoreUnknown = true) public class HardwareConfig { @@ -29,8 +31,13 @@ public class HardwareConfig { public final boolean ledsCanDim; public final ArrayList ledBrightnessRange; public final int ledPWMFrequency; - public final ArrayList statusRGBPins; - public final boolean statusRGBActiveHigh; + public final StatusLEDType statusLEDType; + + @JsonAlias("statusRGBPins") + public final ArrayList statusLEDPins; + + @JsonAlias("statusRGBActiveHigh") + public final boolean statusLEDActiveHigh; // Custom GPIO public final String getGPIOCommand; @@ -49,8 +56,9 @@ public HardwareConfig( boolean ledsCanDim, ArrayList ledBrightnessRange, int ledPwmFrequency, - ArrayList statusRGBPins, - boolean statusRGBActiveHigh, + StatusLEDType statusLEDType, + ArrayList statusLEDPins, + boolean statusLEDActiveHigh, String getGPIOCommand, String setGPIOCommand, String setPWMCommand, @@ -63,8 +71,9 @@ public HardwareConfig( this.ledsCanDim = ledsCanDim; this.ledBrightnessRange = ledBrightnessRange; this.ledPWMFrequency = ledPwmFrequency; - this.statusRGBPins = statusRGBPins; - this.statusRGBActiveHigh = statusRGBActiveHigh; + this.statusLEDType = statusLEDType; + this.statusLEDPins = statusLEDPins; + this.statusLEDActiveHigh = statusLEDActiveHigh; this.getGPIOCommand = getGPIOCommand; this.setGPIOCommand = setGPIOCommand; this.setPWMCommand = setPWMCommand; @@ -80,8 +89,9 @@ public HardwareConfig() { ledsCanDim = false; ledBrightnessRange = new ArrayList<>(); ledPWMFrequency = 0; - statusRGBPins = new ArrayList<>(); - statusRGBActiveHigh = false; + statusLEDType = StatusLEDType.RGB; + statusLEDPins = new ArrayList<>(); + statusLEDActiveHigh = false; getGPIOCommand = ""; setGPIOCommand = ""; setPWMCommand = ""; @@ -121,10 +131,12 @@ public String toString() { + ledBrightnessRange + ", ledPWMFrequency=" + ledPWMFrequency - + ", statusRGBPins=" - + statusRGBPins - + ", statusRGBActiveHigh" - + statusRGBActiveHigh + + ", statusLEDType=" + + statusLEDType + + ", statusLEDPins=" + + statusLEDPins + + ", statusLEDActiveHigh" + + statusLEDActiveHigh + ", getGPIOCommand=" + getGPIOCommand + ", setGPIOCommand=" diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/HardwareManager.java b/photon-core/src/main/java/org/photonvision/common/hardware/HardwareManager.java index e097f2926d..51ceb59d03 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/HardwareManager.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/HardwareManager.java @@ -33,6 +33,7 @@ import org.photonvision.common.dataflow.networktables.NetworkTablesManager; import org.photonvision.common.hardware.gpio.CustomAdapter; import org.photonvision.common.hardware.gpio.CustomDeviceFactory; +import org.photonvision.common.hardware.statusLED.StatusLED; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; import org.photonvision.common.util.ShellExec; @@ -103,12 +104,11 @@ public NativeDeviceFactoryInterface get() { }; statusLED = - hardwareConfig.statusRGBPins.size() == 3 - ? new StatusLED( - lazyDeviceFactory.get(), - hardwareConfig.statusRGBPins, - hardwareConfig.statusRGBActiveHigh) - : null; + StatusLED.ofType( + hardwareConfig.statusLEDType, + lazyDeviceFactory, + hardwareConfig.statusLEDPins, + hardwareConfig.statusLEDActiveHigh); var hasBrightnessRange = hardwareConfig.ledBrightnessRange.size() == 2; visionLED = @@ -162,7 +162,7 @@ public static NativeDeviceFactoryInterface configureCustomGPIO(HardwareConfig ha pinInfo.addGpioPinInfo(pin, pin, List.of(DeviceMode.DIGITAL_OUTPUT)); } } - for (int pin : hardwareConfig.statusRGBPins) { + for (int pin : hardwareConfig.statusLEDPins) { pinInfo.addGpioPinInfo(pin, pin, List.of(DeviceMode.DIGITAL_OUTPUT)); } diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/statusLED/GYStatusLED.java b/photon-core/src/main/java/org/photonvision/common/hardware/statusLED/GYStatusLED.java new file mode 100644 index 0000000000..6af2e43e86 --- /dev/null +++ b/photon-core/src/main/java/org/photonvision/common/hardware/statusLED/GYStatusLED.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.photonvision.common.hardware.statusLED; + +import com.diozero.devices.LED; +import com.diozero.internal.spi.NativeDeviceFactoryInterface; +import java.util.List; +import org.photonvision.common.hardware.PhotonStatus; +import org.photonvision.common.util.TimedTaskManager; + +/** A pair of green and yellow LEDs, as used on the Limelight cameras */ +public class GYStatusLED implements StatusLED { + public final LED greenLED; + public final LED yellowLED; + protected int blinkCounter; + + protected PhotonStatus status = PhotonStatus.GENERIC_ERROR; + + public GYStatusLED( + NativeDeviceFactoryInterface deviceFactory, List statusLedPins, boolean activeHigh) { + // fill unassigned pins with -1 to disable + if (statusLedPins.size() != 3) { + for (int i = 0; i < 3 - statusLedPins.size(); i++) { + statusLedPins.add(-1); + } + } + + // Outputs are active-low for a common-anode RGB LED + greenLED = new LED(deviceFactory, statusLedPins.get(0), activeHigh, false); + yellowLED = new LED(deviceFactory, statusLedPins.get(1), activeHigh, false); + + TimedTaskManager.getInstance().addTask("StatusLEDUpdate", this::updateLED, 75); + } + + protected void setLEDs(boolean green, boolean yellow) { + greenLED.setOn(green); + yellowLED.setOn(yellow); + } + + @Override + public void setStatus(PhotonStatus status) { + this.status = status; + } + + protected void updateLED() { + boolean slowBlink = blinkCounter > 1; + boolean fastBlink = (blinkCounter % 2) > 0; + + switch (status) { + case NT_CONNECTED_TARGETS_VISIBLE -> + // Green fast, yellow on + setLEDs(fastBlink, true); + case NT_CONNECTED_TARGETS_MISSING -> + // Green slow, yellow on + setLEDs(slowBlink, true); + case NT_DISCONNECTED_TARGETS_VISIBLE -> + // Green fast, yellow slow + setLEDs(fastBlink, slowBlink); + case NT_DISCONNECTED_TARGETS_MISSING -> + // Green slow, yellow slow + setLEDs(slowBlink, slowBlink); + case GENERIC_ERROR -> + // No lights + setLEDs(false, false); + } + + blinkCounter++; + blinkCounter %= 6; + } + + @Override + public void close() throws Exception { + greenLED.close(); + yellowLED.close(); + } +} diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/StatusLED.java b/photon-core/src/main/java/org/photonvision/common/hardware/statusLED/RGBStatusLED.java similarity index 92% rename from photon-core/src/main/java/org/photonvision/common/hardware/StatusLED.java rename to photon-core/src/main/java/org/photonvision/common/hardware/statusLED/RGBStatusLED.java index 81a916ae9b..a5c04f9939 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/StatusLED.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/statusLED/RGBStatusLED.java @@ -15,14 +15,16 @@ * along with this program. If not, see . */ -package org.photonvision.common.hardware; +package org.photonvision.common.hardware.statusLED; import com.diozero.devices.LED; import com.diozero.internal.spi.NativeDeviceFactoryInterface; import java.util.List; +import org.photonvision.common.hardware.PhotonStatus; import org.photonvision.common.util.TimedTaskManager; -public class StatusLED implements AutoCloseable { +/** Basic RGB LED with individual control over each pin */ +public class RGBStatusLED implements StatusLED { public final LED redLED; public final LED greenLED; public final LED blueLED; @@ -30,7 +32,7 @@ public class StatusLED implements AutoCloseable { protected PhotonStatus status = PhotonStatus.GENERIC_ERROR; - public StatusLED( + public RGBStatusLED( NativeDeviceFactoryInterface deviceFactory, List statusLedPins, boolean activeHigh) { // fill unassigned pins with -1 to disable if (statusLedPins.size() != 3) { @@ -53,6 +55,7 @@ protected void setRGB(boolean r, boolean g, boolean b) { blueLED.setOn(b); } + @Override public void setStatus(PhotonStatus status) { this.status = status; } diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/statusLED/StatusLED.java b/photon-core/src/main/java/org/photonvision/common/hardware/statusLED/StatusLED.java new file mode 100644 index 0000000000..5a356505b4 --- /dev/null +++ b/photon-core/src/main/java/org/photonvision/common/hardware/statusLED/StatusLED.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.photonvision.common.hardware.statusLED; + +import com.diozero.internal.spi.NativeDeviceFactoryInterface; +import java.util.List; +import java.util.function.Supplier; +import org.jetbrains.annotations.Nullable; +import org.photonvision.common.hardware.PhotonStatus; + +public interface StatusLED extends AutoCloseable { + public void setStatus(PhotonStatus status); + + @Nullable + static StatusLED ofType( + StatusLEDType type, + Supplier lazyDeviceFactory, + List statusLedPins, + boolean activeHigh) { + return switch (type) { + case RGB -> + statusLedPins.size() == 3 + ? new RGBStatusLED(lazyDeviceFactory.get(), statusLedPins, activeHigh) + : null; + case GY -> + statusLedPins.size() == 2 + ? new GYStatusLED(lazyDeviceFactory.get(), statusLedPins, activeHigh) + : null; + }; + } +} diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/statusLED/StatusLEDType.java b/photon-core/src/main/java/org/photonvision/common/hardware/statusLED/StatusLEDType.java new file mode 100644 index 0000000000..b217e5587d --- /dev/null +++ b/photon-core/src/main/java/org/photonvision/common/hardware/statusLED/StatusLEDType.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) Photon Vision. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.photonvision.common.hardware.statusLED; + +import com.fasterxml.jackson.annotation.JsonAlias; + +public enum StatusLEDType { + RGB, + @JsonAlias("Green-Yellow") + GY; +}