Add support for Green/Yellow status LEDs, like is used on some Limelights (#2287)

This commit is contained in:
Alan Everett
2026-05-20 12:46:41 -04:00
committed by GitHub
parent 7e9a67ec6a
commit 02d0f2b047
16 changed files with 387 additions and 72 deletions

View File

@@ -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

View File

@@ -181,4 +181,4 @@ if token:
linkcheck_auth = [(R"https://github.com/.+", token)]
# MyST configuration (https://myst-parser.readthedocs.io/en/latest/configuration.html)
myst_enable_extensions = ["colon_fence", "substitution"]
myst_enable_extensions = ["colon_fence", "substitution", "attrs_inline"]

View File

@@ -3,5 +3,8 @@
"ledPins" : [ 13, 18 ],
"ledsCanDim" : true,
"ledPWMFrequency" : 1000,
"statusLEDType": "GreenYellow",
"statusLEDPins": [ 5, 4 ],
"statusLEDActiveHigh": false,
"vendorFOV" : 75.76079874010732
}

View File

@@ -2,5 +2,8 @@
"deviceName" : "Limelight 2",
"ledPins" : [ 17, 18 ],
"ledsCanDim" : false,
"statusLEDType": "GreenYellow",
"statusLEDPins": [ 5, 4 ],
"statusLEDActiveHigh": false,
"vendorFOV" : 75.76079874010732
}

View File

@@ -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
* `GreenYellow`: A pair of independent green and yellow LEDs
For an explanation of the colors used for status LEDs, see {ref}`Status LEDs<docs/troubleshooting/status-leds: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}",

View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" id="led" width="30" height="30" viewBox="0 0 100 100" style="color-scheme: dark light">
<circle cx="50" cy="50" r="20" fill="dimgrey"/>
<circle cx="50" cy="50" r="20" stroke="black" stroke-width="5" fill="currentColor"/>
<line x1="5" y1="50" x2="20" y2="50" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
<line x1="27.5" y1="11.03" x2="35" y2="24.02" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
<line x1="72.5" y1="11.03" x2="65" y2="24.02" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
<line x1="80" y1="50" x2="95" y2="50" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
<line x1="72.5" y1="88.97" x2="65" y2="75.98" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
<line x1="27.5" y1="88.97" x2="35" y2="75.98" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 927 B

View File

@@ -5,6 +5,7 @@
common-errors
logging
status-leds
camera-troubleshooting
networking-troubleshooting
unix-commands

View File

@@ -0,0 +1,101 @@
---
myst:
substitutions:
led_loader: |
```{image} images/led.svg
:height: 0
```
led: |
```{raw} html
<svg class="led" height="30" width="30">
<use href="../../_images/led.svg#led"/>
</svg>
```
---
<!-- markdownlint-disable-next-line MD033 MD041 -->
<style>
svg.led {
--off-color: transparent;
color: var(--on-color);
}
@keyframes led-blink {
66% {
color: var(--off-color);
}
}
:not(.solid) > svg.led {
animation: led-blink 0.45s steps(1) infinite;
}
@keyframes led-even-blink {
50% {
color: var(--off-color);
}
}
:not(.solid).fast > svg.led {
animation-name: led-even-blink;
animation-duration: 150ms;
}
:not(.solid).error > svg.led {
animation-name: led-even-blink;
animation-duration: 0.90s;
}
.green > svg.led {
--on-color: limegreen;
}
.blue > svg.led {
--on-color: blue;
}
.yellow > svg.led {
--on-color: yellow;
}
.red > svg.led {
--on-color: red;
}
.anti-yellow > svg.led {
--on-color: transparent;
--off-color: yellow;
}
.off > svg.led {
color: var(--off-color);
}
</style>
# 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 | Alternating | [{{ led }}]{.green .error} [{{ led }}]{.anti-yellow .error} | Initializing or faulted, not running
Both | Off | [{{ led }}]{.off} [{{ led }}]{.off} | No power or initialization fault, not running
{{ led_loader }}

View File

@@ -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<Integer> ledBrightnessRange;
public final int ledPWMFrequency;
public final ArrayList<Integer> statusRGBPins;
public final boolean statusRGBActiveHigh;
public final StatusLEDType statusLEDType;
@JsonAlias("statusRGBPins")
public final ArrayList<Integer> statusLEDPins;
@JsonAlias("statusRGBActiveHigh")
public final boolean statusLEDActiveHigh;
// Custom GPIO
public final String getGPIOCommand;
@@ -49,8 +56,9 @@ public class HardwareConfig {
boolean ledsCanDim,
ArrayList<Integer> ledBrightnessRange,
int ledPwmFrequency,
ArrayList<Integer> statusRGBPins,
boolean statusRGBActiveHigh,
StatusLEDType statusLEDType,
ArrayList<Integer> statusLEDPins,
boolean statusLEDActiveHigh,
String getGPIOCommand,
String setGPIOCommand,
String setPWMCommand,
@@ -63,8 +71,9 @@ public class 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 class 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 class HardwareConfig {
+ ledBrightnessRange
+ ", ledPWMFrequency="
+ ledPWMFrequency
+ ", statusRGBPins="
+ statusRGBPins
+ ", statusRGBActiveHigh"
+ statusRGBActiveHigh
+ ", statusLEDType="
+ statusLEDType
+ ", statusLEDPins="
+ statusLEDPins
+ ", statusLEDActiveHigh"
+ statusLEDActiveHigh
+ ", getGPIOCommand="
+ getGPIOCommand
+ ", setGPIOCommand="

View File

@@ -24,6 +24,7 @@ import com.diozero.sbc.DeviceFactoryHelper;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import org.photonvision.common.configuration.ConfigManager;
@@ -33,6 +34,7 @@ import org.photonvision.common.dataflow.networktables.NTDataChangeListener;
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;
@@ -48,18 +50,15 @@ public class HardwareManager {
private final HardwareConfig hardwareConfig;
private final HardwareSettings hardwareSettings;
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private final StatusLED statusLED;
private final Optional<StatusLED> statusLED;
@SuppressWarnings("FieldCanBeLocal")
private final IntegerSubscriber ledModeRequest;
private final IntegerPublisher ledModeState;
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private final NTDataChangeListener ledModeListener;
private final Optional<NTDataChangeListener> ledModeListener;
public final VisionLED visionLED; // May be null if no LED is specified
public final Optional<VisionLED> visionLED;
public static HardwareManager getInstance() {
if (instance == null) {
@@ -102,40 +101,44 @@ public class HardwareManager {
};
statusLED =
hardwareConfig.statusRGBPins.size() == 3
? new StatusLED(
lazyDeviceFactory.get(),
hardwareConfig.statusRGBPins,
hardwareConfig.statusRGBActiveHigh)
: null;
hardwareConfig.statusLEDPins.isEmpty()
? Optional.empty()
: Optional.of(
StatusLED.ofType(
hardwareConfig.statusLEDType,
lazyDeviceFactory,
hardwareConfig.statusLEDPins,
hardwareConfig.statusLEDActiveHigh));
var hasBrightnessRange = hardwareConfig.ledBrightnessRange.size() == 2;
visionLED =
hardwareConfig.ledPins.isEmpty()
? null
: new VisionLED(
lazyDeviceFactory.get(),
hardwareConfig.ledPins,
hardwareConfig.ledsCanDim,
hasBrightnessRange ? hardwareConfig.ledBrightnessRange.get(0) : 0,
hasBrightnessRange ? hardwareConfig.ledBrightnessRange.get(1) : 100,
hardwareConfig.ledPWMFrequency,
ledModeState::set);
? Optional.empty()
: Optional.of(
new VisionLED(
lazyDeviceFactory.get(),
hardwareConfig.ledPins,
hardwareConfig.ledsCanDim,
hasBrightnessRange ? hardwareConfig.ledBrightnessRange.get(0) : 0,
hasBrightnessRange ? hardwareConfig.ledBrightnessRange.get(1) : 100,
hardwareConfig.ledPWMFrequency,
ledModeState::set));
ledModeListener =
visionLED == null
? null
: new NTDataChangeListener(
NetworkTablesManager.getInstance().kRootTable.getInstance(),
ledModeRequest,
visionLED::onLedModeChange);
visionLED.map(
visionLED ->
new NTDataChangeListener(
NetworkTablesManager.getInstance().kRootTable.getInstance(),
ledModeRequest,
visionLED::onLedModeChange));
Runtime.getRuntime().addShutdownHook(new Thread(this::onJvmExit));
if (visionLED != null) {
visionLED.setBrightness(hardwareSettings.ledBrightnessPercentage);
visionLED.blink(85, 4); // bootup blink
}
visionLED.ifPresent(
visionLED -> {
visionLED.setBrightness(hardwareSettings.ledBrightnessPercentage);
visionLED.blink(85, 4); // bootup blink
});
// Start hardware metrics thread (Disabled until implemented)
// if (Platform.isLinux()) MetricsPublisher.getInstance().startTask();
@@ -161,7 +164,7 @@ public class HardwareManager {
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));
}
@@ -171,7 +174,7 @@ public class HardwareManager {
public void setBrightnessPercent(int percent) {
if (percent != hardwareSettings.ledBrightnessPercentage) {
hardwareSettings.ledBrightnessPercentage = percent;
if (visionLED != null) visionLED.setBrightness(percent);
visionLED.ifPresent(visionLED -> visionLED.setBrightness(percent));
ConfigManager.getInstance().requestSave();
logger.info("Setting led brightness to " + percent + "%");
}
@@ -179,7 +182,7 @@ public class HardwareManager {
private void onJvmExit() {
logger.info("Shutting down LEDs...");
if (visionLED != null) visionLED.setState(false);
visionLED.ifPresent(visionLED -> visionLED.setState(false));
ConfigManager.getInstance().onJvmExit();
}
@@ -220,16 +223,16 @@ public class HardwareManager {
updateStatus();
}
public void setError(PhotonStatus status) {
if (status == null || !status.isError()) {
public void setError(Optional<PhotonStatus> status) {
if (status.isEmpty() || !status.get().isError()) {
updateStatus();
} else if (statusLED != null) {
statusLED.setStatus(status);
} else {
statusLED.ifPresent(statusLED -> statusLED.setStatus(status.get()));
}
}
private void updateStatus() {
if (statusLED == null) {
if (statusLED.isEmpty()) {
return;
}
PhotonStatus status;
@@ -247,6 +250,6 @@ public class HardwareManager {
status = PhotonStatus.NT_DISCONNECTED_TARGETS_MISSING;
}
}
statusLED.setStatus(status);
statusLED.ifPresent(statusLED -> statusLED.setStatus(status));
}
}

View File

@@ -0,0 +1,99 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package org.photonvision.common.hardware.statusLED;
import com.diozero.devices.LED;
import com.diozero.internal.spi.NativeDeviceFactoryInterface;
import java.util.Collections;
import java.util.List;
import org.photonvision.common.hardware.PhotonStatus;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.TimedTaskManager;
/** A pair of green and yellow LEDs, as used on the Limelight cameras */
public class GreenYellowStatusLED implements StatusLED {
private final Logger logger = new Logger(GreenYellowStatusLED.class, LogGroup.General);
public final LED greenLED;
public final LED yellowLED;
protected int blinkCounter;
protected PhotonStatus status = PhotonStatus.GENERIC_ERROR;
public GreenYellowStatusLED(
NativeDeviceFactoryInterface deviceFactory, List<Integer> statusLedPins, boolean activeHigh) {
if (statusLedPins.size() != 2) {
logger.warn(
pinErrorTemplate.formatted(2, "Green and Yellow status LEDs", statusLedPins.size()));
}
// fill unassigned pins with -1 to disable
if (statusLedPins.size() < 2) {
statusLedPins.addAll(Collections.nCopies(statusLedPins.size() - 2, -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 % 6) > 1;
boolean fastBlink = (blinkCounter % 2) > 0;
boolean errorBlink = blinkCounter > 5;
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 ->
// Extra slow alternating blink
setLEDs(errorBlink, !errorBlink);
}
blinkCounter++;
blinkCounter %= 12;
}
@Override
public void close() throws Exception {
greenLED.close();
yellowLED.close();
}
}

View File

@@ -15,14 +15,21 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
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.Collections;
import java.util.List;
import org.photonvision.common.hardware.PhotonStatus;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
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 {
private final Logger logger = new Logger(RGBStatusLED.class, LogGroup.General);
public final LED redLED;
public final LED greenLED;
public final LED blueLED;
@@ -30,13 +37,14 @@ public class StatusLED implements AutoCloseable {
protected PhotonStatus status = PhotonStatus.GENERIC_ERROR;
public StatusLED(
public RGBStatusLED(
NativeDeviceFactoryInterface deviceFactory, List<Integer> 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);
}
logger.warn(pinErrorTemplate.formatted(3, "a RGB status LED", statusLedPins.size()));
}
// fill unassigned pins with -1 to disable
if (statusLedPins.size() < 3) {
statusLedPins.addAll(Collections.nCopies(statusLedPins.size() - 3, -1));
}
// Outputs are active-low for a common-anode RGB LED
@@ -53,6 +61,7 @@ public class StatusLED implements AutoCloseable {
blueLED.setOn(b);
}
@Override
public void setStatus(PhotonStatus status) {
this.status = status;
}

View File

@@ -0,0 +1,42 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package org.photonvision.common.hardware.statusLED;
import com.diozero.internal.spi.NativeDeviceFactoryInterface;
import java.util.List;
import java.util.function.Supplier;
import org.photonvision.common.hardware.PhotonStatus;
public interface StatusLED extends AutoCloseable {
static final String pinErrorTemplate =
"Expected %d pins for %s, but found %n pins; unassigned pins will be skipped, extra pins will be ignored";
public void setStatus(PhotonStatus status);
static StatusLED ofType(
StatusLEDType type,
Supplier<NativeDeviceFactoryInterface> lazyDeviceFactory,
List<Integer> statusLedPins,
boolean activeHigh) {
return switch (type) {
case RGB -> new RGBStatusLED(lazyDeviceFactory.get(), statusLedPins, activeHigh);
case GreenYellow ->
new GreenYellowStatusLED(lazyDeviceFactory.get(), statusLedPins, activeHigh);
};
}
}

View File

@@ -0,0 +1,23 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package org.photonvision.common.hardware.statusLED;
public enum StatusLEDType {
RGB,
GreenYellow;
}

View File

@@ -182,7 +182,10 @@ public class VisionModule {
if (HardwareManager.getInstance().visionLED != null && this.camShouldControlLEDs()) {
HardwareManager.getInstance()
.visionLED
.setPipelineModeSupplier(() -> pipelineManager.getCurrentPipelineSettings().ledMode);
.ifPresent(
(visionLED) ->
visionLED.setPipelineModeSupplier(
() -> pipelineManager.getCurrentPipelineSettings().ledMode));
setVisionLEDs(pipelineManager.getCurrentPipelineSettings().ledMode);
}
@@ -517,8 +520,9 @@ public class VisionModule {
}
private void setVisionLEDs(boolean on) {
if (camShouldControlLEDs() && HardwareManager.getInstance().visionLED != null)
HardwareManager.getInstance().visionLED.setState(on);
if (camShouldControlLEDs()) {
HardwareManager.getInstance().visionLED.ifPresent((visionLED) -> visionLED.setState(on));
}
}
public void saveModule() {

View File

@@ -21,6 +21,7 @@ import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.apache.commons.cli.*;
import org.opencv.core.Size;
import org.photonvision.common.LoadJNI;
@@ -355,7 +356,7 @@ public class Main {
VisionSourceManager.getInstance().registerTimedTasks();
logger.info("Starting server...");
HardwareManager.getInstance().setError(null);
HardwareManager.getInstance().setError(Optional.empty());
Server.initialize(DEFAULT_WEBPORT);
}
}