diff --git a/docs/source/docs/hardware/customhardware.md b/docs/source/docs/hardware/customhardware.md index b18c54e5ef..99ba9144d9 100644 --- a/docs/source/docs/hardware/customhardware.md +++ b/docs/source/docs/hardware/customhardware.md @@ -40,6 +40,36 @@ The following diagram shows the GPIO pin numbering of the 40-pin header on Raspb :alt: Raspberry Pi GPIO Pinout ``` +::: + +:::{tab-item} Orange Pi + +Using numbers to identify Orange Pi pins is cumbersome, so it is best to specify the pins by their names using strings instead (eg. `"GPIO1_B7"`). The mapping between GPIO lines and physical pins varies depending on which Orange Pi model you are using. The below diagrams show the pin names for the Orange Pi 5 series. + +Orange Pi 5/5B ([Details](http://www.orangepi.org/orangepiwiki/index.php/26_Pin_Interface_Pin_Description)): + +```{image} images/opi5-pinout.png +:alt: Orange Pi 5 Pinout +``` + +Orange Pi 5 Plus ([User Manual](https://drive.google.com/drive/folders/1Ov3mZqnMOf_8wpNt9rDxoGIR1ray2iiy)): + +```{image} images/opi5-plus-pinout.png +:alt: Orange Pi 5 Plus Pinout +``` + +Orange Pi 5 Pro ([User Manual](https://drive.google.com/drive/folders/1j3gmf31XBuKPBeNIQOqqh9X_7SFCOv0s)): + +```{image} images/opi5-pro-pinout.png +:alt: Orange Pi 5 Pro Pinout +``` + +Orange Pi 5 Max ([User Manual](https://drive.google.com/drive/folders/1kzMRI95yaXLbQuK86fUbs92NJ6QOYIGO)): + +```{image} images/opi5-max-pinout.png +:alt: Orange Pi 5 Max Pinout +``` + ::: :::: diff --git a/docs/source/docs/hardware/images/opi5-max-pinout.png b/docs/source/docs/hardware/images/opi5-max-pinout.png new file mode 100644 index 0000000000..c24a474788 Binary files /dev/null and b/docs/source/docs/hardware/images/opi5-max-pinout.png differ diff --git a/docs/source/docs/hardware/images/opi5-pinout.png b/docs/source/docs/hardware/images/opi5-pinout.png new file mode 100644 index 0000000000..8e0fd9793f Binary files /dev/null and b/docs/source/docs/hardware/images/opi5-pinout.png differ diff --git a/docs/source/docs/hardware/images/opi5-plus-pinout.png b/docs/source/docs/hardware/images/opi5-plus-pinout.png new file mode 100644 index 0000000000..d057aeca1b Binary files /dev/null and b/docs/source/docs/hardware/images/opi5-plus-pinout.png differ diff --git a/docs/source/docs/hardware/images/opi5-pro-pinout.png b/docs/source/docs/hardware/images/opi5-pro-pinout.png new file mode 100644 index 0000000000..519f5318ac Binary files /dev/null and b/docs/source/docs/hardware/images/opi5-pro-pinout.png differ 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..15d2e12689 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 @@ -19,17 +19,18 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import java.util.ArrayList; +import org.photonvision.common.hardware.gpio.PinIdentifier; @JsonIgnoreProperties(ignoreUnknown = true) public class HardwareConfig { public final String deviceName; // LED control - public final ArrayList ledPins; + public final ArrayList ledPins; public final boolean ledsCanDim; public final ArrayList ledBrightnessRange; public final int ledPWMFrequency; - public final ArrayList statusRGBPins; + public final ArrayList statusRGBPins; public final boolean statusRGBActiveHigh; // Custom GPIO @@ -45,11 +46,11 @@ public class HardwareConfig { public HardwareConfig( String deviceName, - ArrayList ledPins, + ArrayList ledPins, boolean ledsCanDim, ArrayList ledBrightnessRange, int ledPwmFrequency, - ArrayList statusRGBPins, + ArrayList statusRGBPins, boolean statusRGBActiveHigh, String getGPIOCommand, String 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 98e223c124..4e4f9c1928 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 @@ -18,10 +18,12 @@ package org.photonvision.common.hardware; import com.diozero.api.DeviceMode; +import com.diozero.api.PinInfo; import com.diozero.internal.spi.NativeDeviceFactoryInterface; import com.diozero.sbc.BoardPinInfo; import com.diozero.sbc.DeviceFactoryHelper; import java.io.IOException; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -33,6 +35,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.gpio.PinIdentifier; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; import org.photonvision.common.util.ShellExec; @@ -103,7 +106,7 @@ public NativeDeviceFactoryInterface get() { statusLED = hardwareConfig.statusRGBPins.size() == 3 - ? new StatusLED( + ? StatusLED.create( lazyDeviceFactory.get(), hardwareConfig.statusRGBPins, hardwareConfig.statusRGBActiveHigh) @@ -154,20 +157,42 @@ public static NativeDeviceFactoryInterface configureCustomGPIO(HardwareConfig ha BoardPinInfo pinInfo = deviceFactory.getBoardPinInfo(); // Populate pin info according to hardware config - for (int pin : hardwareConfig.ledPins) { + for (PinIdentifier pin : hardwareConfig.ledPins) { if (hardwareConfig.ledsCanDim) { - pinInfo.addGpioPinInfo(pin, pin, List.of(DeviceMode.PWM_OUTPUT, DeviceMode.DIGITAL_OUTPUT)); + addCustomGPIOPin(pinInfo, pin, List.of(DeviceMode.PWM_OUTPUT, DeviceMode.DIGITAL_OUTPUT)); } else { - pinInfo.addGpioPinInfo(pin, pin, List.of(DeviceMode.DIGITAL_OUTPUT)); + addCustomGPIOPin(pinInfo, pin, List.of(DeviceMode.DIGITAL_OUTPUT)); } } - for (int pin : hardwareConfig.statusRGBPins) { - pinInfo.addGpioPinInfo(pin, pin, List.of(DeviceMode.DIGITAL_OUTPUT)); + for (PinIdentifier pin : hardwareConfig.statusRGBPins) { + addCustomGPIOPin(pinInfo, pin, List.of(DeviceMode.DIGITAL_OUTPUT)); } return deviceFactory; } + /* + * This is used to generate integer IDs for custom named pins, deep in the + * unused range for gpio numbers. These IDs will still be recognized as a + * normal gpio number by most of diozero's code, but not by PinIdentifier. + */ + static int namedPinID = Integer.MIN_VALUE; + + protected static PinInfo addCustomGPIOPin( + BoardPinInfo pinInfo, PinIdentifier pin, Collection modes) { + if (pin instanceof PinIdentifier.NumberedPin) { + int number = pin.getDeviceNumber(); + return pinInfo.addGpioPinInfo(number, number, modes); + } else if (pin instanceof PinIdentifier.NamedPin) { + int number = namedPinID++; + String name = ((PinIdentifier.NamedPin) pin).name; + return pinInfo.addGpioPinInfo(number, name, number, modes); + } else { + throw new UnsupportedOperationException( + "Only numbered or named pins can be used with custom GPIO"); + } + } + public void setBrightnessPercent(int percent) { if (percent != hardwareSettings.ledBrightnessPercentage) { hardwareSettings.ledBrightnessPercentage = percent; 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.java index 81a916ae9b..2e34af791b 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/StatusLED.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/StatusLED.java @@ -17,9 +17,12 @@ package org.photonvision.common.hardware; +import com.diozero.api.NoSuchDeviceException; import com.diozero.devices.LED; import com.diozero.internal.spi.NativeDeviceFactoryInterface; import java.util.List; +import org.jetbrains.annotations.Nullable; +import org.photonvision.common.hardware.gpio.PinIdentifier; import org.photonvision.common.util.TimedTaskManager; public class StatusLED implements AutoCloseable { @@ -31,22 +34,52 @@ public class StatusLED implements AutoCloseable { protected PhotonStatus status = PhotonStatus.GENERIC_ERROR; public StatusLED( - NativeDeviceFactoryInterface deviceFactory, List statusLedPins, boolean activeHigh) { + NativeDeviceFactoryInterface deviceFactory, + List statusLedPins, + boolean activeHigh) + throws NoSuchDeviceException { // fill unassigned pins with -1 to disable if (statusLedPins.size() != 3) { for (int i = 0; i < 3 - statusLedPins.size(); i++) { - statusLedPins.add(-1); + statusLedPins.add(PinIdentifier.numbered(-1)); } } // Outputs are active-low for a common-anode RGB LED - redLED = new LED(deviceFactory, statusLedPins.get(0), activeHigh, false); - greenLED = new LED(deviceFactory, statusLedPins.get(1), activeHigh, false); - blueLED = new LED(deviceFactory, statusLedPins.get(2), activeHigh, false); + redLED = + new LED( + deviceFactory, + statusLedPins.get(0).info(deviceFactory).getDeviceNumber(), + activeHigh, + false); + greenLED = + new LED( + deviceFactory, + statusLedPins.get(1).info(deviceFactory).getDeviceNumber(), + activeHigh, + false); + blueLED = + new LED( + deviceFactory, + statusLedPins.get(2).info(deviceFactory).getDeviceNumber(), + activeHigh, + false); TimedTaskManager.getInstance().addTask("StatusLEDUpdate", this::updateLED, 150); } + @Nullable + static StatusLED create( + NativeDeviceFactoryInterface deviceFactory, + List statusLedPins, + boolean activeHigh) { + try { + return new StatusLED(deviceFactory, statusLedPins, activeHigh); + } catch (NoSuchDeviceException e) { + return null; + } + } + protected void setRGB(boolean r, boolean g, boolean b) { redLED.setOn(r); greenLED.setOn(g); diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/VisionLED.java b/photon-core/src/main/java/org/photonvision/common/hardware/VisionLED.java index 926091ca83..f49eda7d60 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/VisionLED.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/VisionLED.java @@ -17,15 +17,17 @@ package org.photonvision.common.hardware; +import com.diozero.api.NoSuchDeviceException; +import com.diozero.api.PinInfo; import com.diozero.devices.LED; import com.diozero.devices.PwmLed; import com.diozero.internal.spi.NativeDeviceFactoryInterface; -import com.diozero.sbc.BoardPinInfo; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BooleanSupplier; import java.util.function.Consumer; +import org.photonvision.common.hardware.gpio.PinIdentifier; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; import org.photonvision.common.util.TimedTaskManager; @@ -51,7 +53,7 @@ public class VisionLED implements AutoCloseable { public VisionLED( NativeDeviceFactoryInterface deviceFactory, - List ledPins, + List ledPins, boolean ledsCanDim, int brightnessMin, int brightnessMax, @@ -63,17 +65,23 @@ public VisionLED( if (pwmFrequency > 0) { deviceFactory.setBoardPwmFrequency(pwmFrequency); } - BoardPinInfo boardPinInfo = deviceFactory.getBoardPinInfo(); ledPins.forEach( pin -> { - if (ledsCanDim && boardPinInfo.getByPwmOrGpioNumberOrThrow(pin).isPwmOutputSupported()) { - PwmLed led = new PwmLed(deviceFactory, pin); - if (pwmFrequency > 0) { - led.setPwmFrequency(pwmFrequency); + PinInfo pinInfo = pin.info(deviceFactory); + if (ledsCanDim && pinInfo.isPwmOutputSupported()) { + try { + PwmLed led = new PwmLed(deviceFactory, pinInfo.getPwmNum()); + if (pwmFrequency > 0) { + led.setPwmFrequency(pwmFrequency); + } + dimmableVisionLEDs.add(led); + } catch (NoSuchDeviceException e) { } - dimmableVisionLEDs.add(led); } else { - visionLEDs.add(new LED(deviceFactory, pin)); + try { + visionLEDs.add(new LED(deviceFactory, pinInfo.getDeviceNumber(), true, false)); + } catch (NoSuchDeviceException e) { + } } }); pipelineModeSupplier = () -> false; diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomAdapter.java b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomAdapter.java index 9cdc0d4f05..b2825cdf57 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomAdapter.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomAdapter.java @@ -55,33 +55,26 @@ protected static String execute(String command) { return runCommand.get().getOutput(); } - public boolean getGPIO(int gpio) { - return Boolean.parseBoolean( - execute(getGPIOCommand.replace("{p}", Integer.toString(gpio))).trim()); + public boolean getGPIO(PinIdentifier gpio) { + return Boolean.parseBoolean(execute(getGPIOCommand.replace("{p}", gpio.toString())).trim()); } - public void setGPIO(int gpio, boolean state) { - execute( - setGPIOCommand - .replace("{p}", Integer.toString(gpio)) - .replace("{s}", Boolean.toString(state))); + public void setGPIO(PinIdentifier gpio, boolean state) { + execute(setGPIOCommand.replace("{p}", gpio.toString()).replace("{s}", Boolean.toString(state))); } - public void setPWM(int gpio, double value) { - execute( - setPWMCommand - .replace("{p}", Integer.toString(gpio)) - .replace("{v}", Double.toString(value))); + public void setPWM(PinIdentifier gpio, double value) { + execute(setPWMCommand.replace("{p}", gpio.toString()).replace("{v}", Double.toString(value))); } - public void setPwmFrequency(int gpio, int frequency) { + public void setPwmFrequency(PinIdentifier gpio, int frequency) { execute( setPWMFrequencyCommand - .replace("{p}", Integer.toString(gpio)) + .replace("{p}", gpio.toString()) .replace("{f}", Integer.toString(frequency))); } - public void releaseGPIO(int gpio) { - execute(releaseGPIOCommand.replace("{p}", Integer.toString(gpio))); + public void releaseGPIO(PinIdentifier gpio) { + execute(releaseGPIOCommand.replace("{p}", gpio.toString())); } } diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDeviceFactory.java b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDeviceFactory.java index 76c04275ad..49cf8dedb1 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDeviceFactory.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDeviceFactory.java @@ -87,7 +87,7 @@ public String getName() { @Override public int getGpioValue(int gpio) { - return adapter.getGPIO(gpio) ? 1 : 0; + return adapter.getGPIO(PinIdentifier.numbered(gpio)) ? 1 : 0; } @Override @@ -118,26 +118,26 @@ public void setBoardServoFrequency(int servoFrequency) { @Override public GpioDigitalInputDeviceInterface createDigitalInputDevice( String key, PinInfo pinInfo, GpioPullUpDown pud, GpioEventTrigger trigger) { - return new CustomDigitalInputDevice(this, key, pinInfo.getDeviceNumber(), pud, trigger); + return new CustomDigitalInputDevice(this, key, PinIdentifier.fromInfo(pinInfo), pud, trigger); } @Override public GpioDigitalOutputDeviceInterface createDigitalOutputDevice( String key, PinInfo pinInfo, boolean initialValue) { - return new CustomDigitalOutputDevice(this, key, pinInfo.getDeviceNumber(), initialValue); + return new CustomDigitalOutputDevice(this, key, PinIdentifier.fromInfo(pinInfo), initialValue); } @Override public GpioDigitalInputOutputDeviceInterface createDigitalInputOutputDevice( String key, PinInfo pinInfo, DeviceMode mode) { - return new CustomDigitalInputOutputDevice(this, key, pinInfo.getDeviceNumber(), mode); + return new CustomDigitalInputOutputDevice(this, key, PinIdentifier.fromInfo(pinInfo), mode); } @Override public InternalPwmOutputDeviceInterface createPwmOutputDevice( String key, PinInfo pinInfo, int pwmFrequency, float initialValue) { return new CustomPwmOutputDevice( - this, key, pinInfo.getDeviceNumber(), pwmFrequency, initialValue); + this, key, PinIdentifier.fromInfo(pinInfo), pwmFrequency, initialValue); } @Override diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDigitalInputDevice.java b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDigitalInputDevice.java index 0b5372080d..3ad8b8d183 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDigitalInputDevice.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDigitalInputDevice.java @@ -27,12 +27,12 @@ public class CustomDigitalInputDevice extends AbstractInputDevice implements GpioDigitalInputDeviceInterface { protected final CustomAdapter adapter; - protected final int gpio; + protected final PinIdentifier gpio; public CustomDigitalInputDevice( CustomDeviceFactory deviceFactory, String key, - int gpio, + PinIdentifier gpio, GpioPullUpDown pud, GpioEventTrigger trigger) { super(key, deviceFactory); @@ -48,7 +48,7 @@ public boolean getValue() throws RuntimeIOException { @Override public int getGpio() { - return gpio; + return gpio.getDeviceNumber(); } @Override diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDigitalInputOutputDevice.java b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDigitalInputOutputDevice.java index c18e6f4019..dfe47df266 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDigitalInputOutputDevice.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDigitalInputOutputDevice.java @@ -26,11 +26,11 @@ public class CustomDigitalInputOutputDevice extends AbstractInputDevice implements GpioDigitalInputOutputDeviceInterface { protected final CustomAdapter adapter; - protected final int gpio; + protected final PinIdentifier gpio; private boolean outputValue = false; public CustomDigitalInputOutputDevice( - CustomDeviceFactory deviceFactory, String key, int gpio, DeviceMode mode) { + CustomDeviceFactory deviceFactory, String key, PinIdentifier gpio, DeviceMode mode) { super(key, deviceFactory); this.adapter = deviceFactory.adapter; @@ -52,7 +52,7 @@ public boolean getValue() throws RuntimeIOException { @Override public int getGpio() { - return gpio; + return gpio.getDeviceNumber(); } @Override diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDigitalOutputDevice.java b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDigitalOutputDevice.java index 6fb5e7f596..9048ed2a4b 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDigitalOutputDevice.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomDigitalOutputDevice.java @@ -24,11 +24,11 @@ public class CustomDigitalOutputDevice extends AbstractDevice implements GpioDigitalOutputDeviceInterface { protected final CustomAdapter adapter; - protected final int gpio; + protected final PinIdentifier gpio; private boolean state; public CustomDigitalOutputDevice( - CustomDeviceFactory deviceFactory, String key, int gpio, boolean initialValue) { + CustomDeviceFactory deviceFactory, String key, PinIdentifier gpio, boolean initialValue) { super(key, deviceFactory); this.adapter = deviceFactory.adapter; @@ -45,7 +45,7 @@ public boolean getValue() throws RuntimeIOException { @Override public int getGpio() { - return gpio; + return gpio.getDeviceNumber(); } @Override diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomPwmOutputDevice.java b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomPwmOutputDevice.java index 0ee530b54f..6378a2fb5b 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomPwmOutputDevice.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/CustomPwmOutputDevice.java @@ -24,14 +24,14 @@ public class CustomPwmOutputDevice extends AbstractDevice implements InternalPwmOutputDeviceInterface { protected final CustomAdapter adapter; - protected final int gpio; + protected final PinIdentifier gpio; private float state; private int frequency; public CustomPwmOutputDevice( CustomDeviceFactory deviceFactory, String key, - int gpio, + PinIdentifier gpio, int pwmFrequency, float initialValue) { super(key, deviceFactory); @@ -45,12 +45,12 @@ public CustomPwmOutputDevice( @Override public int getGpio() { - return gpio; + return gpio.getDeviceNumber(); } @Override public int getPwmNum() { - return gpio; + return gpio.getDeviceNumber(); } @Override diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/gpio/PinIdentifier.java b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/PinIdentifier.java new file mode 100644 index 0000000000..905d578635 --- /dev/null +++ b/photon-core/src/main/java/org/photonvision/common/hardware/gpio/PinIdentifier.java @@ -0,0 +1,138 @@ +/* + * 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.gpio; + +import com.diozero.api.NoSuchDeviceException; +import com.diozero.api.PinInfo; +import com.diozero.internal.spi.NativeDeviceFactoryInterface; +import com.diozero.sbc.DeviceFactoryHelper; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public interface PinIdentifier { + public static final class NamedPin implements PinIdentifier { + public final String name; + + @JsonCreator + protected NamedPin(String name) { + this.name = name; + } + + @Override + public PinInfo info(NativeDeviceFactoryInterface deviceFactory) throws NoSuchDeviceException { + PinInfo pinInfo = deviceFactory.getBoardPinInfo().getByName(name); + if (pinInfo == null) { + throw new NoSuchDeviceException("No such GPIO named \"" + name + "\""); + } + return pinInfo; + } + + @JsonValue + public String toValue() { + return name; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof NamedPin) { + return this.name.equals(((NamedPin) obj).name); + } else { + return super.equals(obj); + } + } + + @Override + public int getDeviceNumber() { + return PinInfo.NOT_DEFINED; + } + + @Override + public String toString() { + return this.name; + } + } + + public static final class NumberedPin implements PinIdentifier { + public final int number; + + @JsonCreator + protected NumberedPin(int number) { + this.number = number; + } + + @Override + public PinInfo info(NativeDeviceFactoryInterface deviceFactory) throws NoSuchDeviceException { + return deviceFactory.getBoardPinInfo().getByGpioNumberOrThrow(number); + } + + @JsonValue + public int toValue() { + return number; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof NumberedPin) { + return this.number == ((NumberedPin) obj).number; + } else { + return super.equals(obj); + } + } + + @Override + public int getDeviceNumber() { + return number; + } + + @Override + public String toString() { + return Integer.toString(number); + } + } + + @JsonCreator + public static PinIdentifier named(String name) { + return new NamedPin(name); + } + + @JsonCreator + public static PinIdentifier numbered(int number) { + return new NumberedPin(number); + } + + public static PinIdentifier fromInfo(PinInfo info) throws NoSuchDeviceException { + int number = info.getDeviceNumber(); + if (number > PinInfo.NOT_DEFINED) { + return numbered(number); + } + String name = info.getName(); + if (name != "") { + return named(name); + } + throw new NoSuchDeviceException( + "PinIdentifier can only represent pins that are numbered or named"); + } + + public default PinInfo info() throws NoSuchDeviceException { + return info(DeviceFactoryHelper.getNativeDeviceFactory()); + } + + public PinInfo info(NativeDeviceFactoryInterface deviceFactory) throws NoSuchDeviceException; + + public int getDeviceNumber(); +} diff --git a/photon-core/src/main/resources/boarddefs/raspberrypi_5b.txt b/photon-core/src/main/resources/boarddefs/raspberrypi_5b.txt new file mode 100644 index 0000000000..73d3909e39 --- /dev/null +++ b/photon-core/src/main/resources/boarddefs/raspberrypi_5b.txt @@ -0,0 +1,57 @@ +# GPIO Chip Mapping - Chip, Id, Label +Chip, 0, pinctrl-rp1 +#Chip, 10, ignore-chip10 +#Chip, 11, ignore-chip11 +#Chip, 12, ignore-chip12 +#Chip, 13, ignore-chip13 + +# GPIO Header info - General, Header, Physical Pin, Name +General, J8, 1, 3v3 +General, J8, 2, 5v +General, J8, 4, 5v +General, J8, 6, GND +General, J8, 9, GND +General, J8, 14, GND +General, J8, 17, 3v3 +General, J8, 20, GND +General, J8, 25, GND +General, J8, 30, GND +General, J8, 34, GND +General, J8, 39, GND + +# For enabling sysfs PWM, see +# https://jumpnowtek.com/rpi/Using-the-Raspberry-Pi-Hardware-PWM-timers.html + +# GPIO, Header, GPIO#, Name, Physical Pin, Chip, Line, Modes +GPIO, J8, 0, ID_SDA, 27, 0, 0, DIGITAL_INPUT | DIGITAL_OUTPUT +GPIO, J8, 1, ID_SCL, 28, 0, 1, DIGITAL_INPUT | DIGITAL_OUTPUT +GPIO, J8, 2, SDA1, 3, 0, 2, DIGITAL_INPUT | DIGITAL_OUTPUT # SDA1 +GPIO, J8, 3, SCL1, 5, 0, 3, DIGITAL_INPUT | DIGITAL_OUTPUT # SCL1 +GPIO, J8, 4, GPIO_GCLK, 7, 0, 4, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO_GCLK +GPIO, J8, 5, GPIO5, 29, 0, 5, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO5 +GPIO, J8, 6, GPIO6, 31, 0, 6, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO6 +GPIO, J8, 7, SPI_CE1_N, 26, 0, 7, DIGITAL_INPUT | DIGITAL_OUTPUT # SPI_CE1_N "spi0 CS1" +GPIO, J8, 8, SPI_CE0_N, 24, 0, 8, DIGITAL_INPUT | DIGITAL_OUTPUT # SPI_CE0_N "spi0 CS0" +GPIO, J8, 9, SPI_MISO, 21, 0, 9, DIGITAL_INPUT | DIGITAL_OUTPUT # SPI_MISO +GPIO, J8, 10, SPI_MOSI, 19, 0, 10, DIGITAL_INPUT | DIGITAL_OUTPUT # SPI_MOSI +GPIO, J8, 11, SPI_SCLK, 23, 0, 11, DIGITAL_INPUT | DIGITAL_OUTPUT # SPI_SCLK +#GPIO, J8, 12, GPIO12, 32, 0, 12, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO12, Alt0 = PWM0 +PWM, J8, 12, GPIO12, 32, 0, 12, 0, 0, DIGITAL_INPUT | DIGITAL_OUTPUT | PWM_OUTPUT # GPIO12, Alt0 = PWM0 +#GPIO, J8, 13, GPIO13, 33, 0, 13, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO13, Alt0 = PWM1 +PWM, J8, 13, GPIO13, 33, 0, 13, 0, 1, DIGITAL_INPUT | DIGITAL_OUTPUT | PWM_OUTPUT # GPIO13, Alt0 = PWM1 +GPIO, J8, 14, TXD1, 8, 0, 14, DIGITAL_INPUT | DIGITAL_OUTPUT # TXD1 +GPIO, J8, 15, RXD1, 10, 0, 15, DIGITAL_INPUT | DIGITAL_OUTPUT # RXD1 +GPIO, J8, 16, GPIO16, 36, 0, 16, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO16, Alt4 = SPI1-CE2 +GPIO, J8, 17, GPIO17, 11, 0, 17, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO17, Alt4 = SPI1-CE1 +#GPIO, J8, 18, GPIO18, 12, 0, 18, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO18, Alt0 = PCM-CLK, Alt4 = SPI1-CE0, Alt5 = PWM0 +PWM, J8, 18, GPIO18, 12, 0, 18, 0, 0, DIGITAL_INPUT | DIGITAL_OUTPUT | PWM_OUTPUT # GPIO18 +#GPIO, J8, 19, GPIO19, 35, 0, 19, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO19, Alt4 = SPI1-MISO, Alt5 = PWM1 +PWM, J8, 19, GPIO19, 35, 0, 19, 0, 1, DIGITAL_INPUT | DIGITAL_OUTPUT | PWM_OUTPUT # GPIO19 +GPIO, J8, 20, GPIO20, 38, 0, 20, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO20, Alt4 = SPI1-MOSI +GPIO, J8, 21, GPIO21, 40, 0, 21, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO21, Alt4 = SPI1-SCLK +GPIO, J8, 22, GPIO22, 15, 0, 22, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO22 +GPIO, J8, 23, GPIO23, 16, 0, 23, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO23 +GPIO, J8, 24, GPIO24, 18, 0, 24, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO24 +GPIO, J8, 25, GPIO25, 22, 0, 25, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO25 +GPIO, J8, 26, GPIO26, 37, 0, 26, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO26 +GPIO, J8, 27, GPIO27, 13, 0, 27, DIGITAL_INPUT | DIGITAL_OUTPUT # GPIO27 \ No newline at end of file diff --git a/photon-core/src/test/java/org/photonvision/hardware/HardwareConfigTest.java b/photon-core/src/test/java/org/photonvision/hardware/HardwareConfigTest.java index 42aaf97f9e..3e7012824c 100644 --- a/photon-core/src/test/java/org/photonvision/hardware/HardwareConfigTest.java +++ b/photon-core/src/test/java/org/photonvision/hardware/HardwareConfigTest.java @@ -28,24 +28,24 @@ import org.photonvision.common.configuration.HardwareConfig; import org.photonvision.common.hardware.HardwareManager; import org.photonvision.common.hardware.gpio.CustomDeviceFactory; +import org.photonvision.common.hardware.gpio.PinIdentifier; import org.photonvision.common.util.TestUtils; public class HardwareConfigTest { @Test - public void loadJson() { - try { - System.out.println("Loading Hardware configs..."); - var config = - new ObjectMapper().readValue(TestUtils.getHardwareConfigJson(), HardwareConfig.class); - assertEquals(config.deviceName, "PhotonVision"); - // Ensure defaults are not null - assertArrayEquals(config.ledPins.stream().mapToInt(i -> i).toArray(), new int[] {2, 13}); - NativeDeviceFactoryInterface deviceFactory = HardwareManager.configureCustomGPIO(config); - assertTrue(deviceFactory instanceof CustomDeviceFactory); - deviceFactory.close(); - - } catch (IOException e) { - e.printStackTrace(); - } + public void loadJson() throws IOException { + System.out.println("Loading Hardware configs..."); + var config = + new ObjectMapper().readValue(TestUtils.getHardwareConfigJson(), HardwareConfig.class); + assertEquals(config.deviceName, "PhotonVision"); + // Ensure defaults are not null + assertArrayEquals( + config.ledPins.toArray(), + new PinIdentifier[] { + PinIdentifier.numbered(2), PinIdentifier.numbered(13), PinIdentifier.named("GPIO1_B7") + }); + NativeDeviceFactoryInterface deviceFactory = HardwareManager.configureCustomGPIO(config); + assertTrue(deviceFactory instanceof CustomDeviceFactory); + deviceFactory.close(); } } diff --git a/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java b/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java index fcff442b76..989a54c7cc 100644 --- a/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java +++ b/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java @@ -35,6 +35,7 @@ import org.photonvision.common.configuration.HardwareConfig; import org.photonvision.common.hardware.HardwareManager; import org.photonvision.common.hardware.VisionLED; +import org.photonvision.common.hardware.gpio.PinIdentifier; import org.photonvision.common.util.TestUtils; public class HardwareTest { @@ -48,7 +49,15 @@ public void testNativeGPIO() { try (NativeDeviceFactoryInterface deviceFactory = new DefaultDeviceFactory()) { Assumptions.assumeTrue(deviceFactory.getBoardInfo().isRecognised()); - try (VisionLED led = new VisionLED(deviceFactory, List.of(2, 13), false, 0, 100, 0, null)) { + try (VisionLED led = + new VisionLED( + deviceFactory, + List.of(PinIdentifier.numbered(2), PinIdentifier.numbered(13)), + false, + 0, + 100, + 0, + null)) { // Verify states can be set led.setState(true); assertEquals(1, deviceFactory.getGpioValue(2)); @@ -75,7 +84,15 @@ void setup() throws IOException { @Test public void testCustomGPIO() throws IOException { - try (VisionLED led = new VisionLED(deviceFactory, List.of(2, 13), false, 0, 100, 0, null)) { + try (VisionLED led = + new VisionLED( + deviceFactory, + List.of(PinIdentifier.numbered(2), PinIdentifier.numbered(13)), + false, + 0, + 100, + 0, + null)) { // Verify states can be set led.setState(true); assertEquals(1, deviceFactory.getGpioValue(2)); @@ -88,7 +105,15 @@ public void testCustomGPIO() throws IOException { @Test public void testBlink() throws InterruptedException, IOException { - try (VisionLED led = new VisionLED(deviceFactory, List.of(2, 13), false, 0, 100, 0, null)) { + try (VisionLED led = + new VisionLED( + deviceFactory, + List.of(PinIdentifier.numbered(2), PinIdentifier.numbered(13)), + false, + 0, + 100, + 0, + null)) { // Verify blinking toggles between states HashSet seenValues = new HashSet<>(); led.blink(125, 3); diff --git a/test-resources/hardware/HardwareConfig.json b/test-resources/hardware/HardwareConfig.json index eb94e71b09..4dc433ab34 100644 --- a/test-resources/hardware/HardwareConfig.json +++ b/test-resources/hardware/HardwareConfig.json @@ -1,6 +1,6 @@ { "deviceName": "PhotonVision", - "ledPins" : [2, 13], + "ledPins" : [2, 13, "GPIO1_B7"], "statusRGBPins" : [-1, -1, -1], "ledsCanDim" : true, "getGPIOCommand": "cat /tmp/GPIO{p}",