mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-19 00:41:41 +00:00
Add support for Green/Yellow status LEDs, like is used on some Limelights (#2287)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -3,5 +3,8 @@
|
||||
"ledPins" : [ 13, 18 ],
|
||||
"ledsCanDim" : true,
|
||||
"ledPWMFrequency" : 1000,
|
||||
"statusLEDType": "GreenYellow",
|
||||
"statusLEDPins": [ 5, 4 ],
|
||||
"statusLEDActiveHigh": false,
|
||||
"vendorFOV" : 75.76079874010732
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
"deviceName" : "Limelight 2",
|
||||
"ledPins" : [ 17, 18 ],
|
||||
"ledsCanDim" : false,
|
||||
"statusLEDType": "GreenYellow",
|
||||
"statusLEDPins": [ 5, 4 ],
|
||||
"statusLEDActiveHigh": false,
|
||||
"vendorFOV" : 75.76079874010732
|
||||
}
|
||||
|
||||
@@ -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}",
|
||||
|
||||
10
docs/source/docs/troubleshooting/images/led.svg
Normal file
10
docs/source/docs/troubleshooting/images/led.svg
Normal 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 |
@@ -5,6 +5,7 @@
|
||||
|
||||
common-errors
|
||||
logging
|
||||
status-leds
|
||||
camera-troubleshooting
|
||||
networking-troubleshooting
|
||||
unix-commands
|
||||
|
||||
101
docs/source/docs/troubleshooting/status-leds.md
Normal file
101
docs/source/docs/troubleshooting/status-leds.md
Normal 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 }}
|
||||
@@ -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="
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user