Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
LED Strip using pi4j
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS org.slf4j:slf4j-api:2.0.3
//DEPS org.slf4j:slf4j-simple:2.0.3
//DEPS com.github.lalyos:jfiglet:0.0.8
//DEPS com.pi4j:pi4j-core:2.2.1
//DEPS com.pi4j:pi4j-plugin-raspberrypi:2.2.1
//DEPS com.pi4j:pi4j-plugin-pigpio:2.2.1
//DEPS com.pi4j:pi4j-plugin-linuxfs:2.2.1
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import com.pi4j.Pi4J;
import com.pi4j.context.Context;
import com.pi4j.io.spi.Spi;
import com.pi4j.io.spi.SpiConfig;
import com.pi4j.io.spi.SpiMode;
import com.pi4j.library.pigpio.PiGpio;
import com.pi4j.plugin.linuxfs.provider.i2c.LinuxFsI2CProvider;
import com.pi4j.plugin.pigpio.provider.gpio.digital.PiGpioDigitalInputProvider;
import com.pi4j.plugin.pigpio.provider.gpio.digital.PiGpioDigitalOutputProvider;
import com.pi4j.plugin.pigpio.provider.pwm.PiGpioPwmProvider;
import com.pi4j.plugin.pigpio.provider.serial.PiGpioSerialProvider;
import com.pi4j.plugin.pigpio.provider.spi.PiGpioSpiProvider;
import com.pi4j.plugin.raspberrypi.platform.RaspberryPiPlatform;
class LedStripSimple {
public static void main(String[] args) {
final var piGpio = PiGpio.newNativeInstance();
var pi4j = Pi4J.newContextBuilder()
.noAutoDetect()
.add(new RaspberryPiPlatform() {
@Override
protected String[] getProviders() {
return new String[] {};
}
})
.add(PiGpioSpiProvider.newInstance(piGpio))
.build();
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
int pixels = 30;
var ledStrip = new LedStripSimple(pi4j, pixels, 0.2);
waitForKey("Initialized");
waitForKey("AllOff");
ledStrip.allOff();
while(true) {
//waitForKey("Set led strip to ORANGE");
ledStrip.setStripColor(PixelColor.ORANGE);
ledStrip.render();
delay(200);
//waitForKey("Set led strip to PINK");
ledStrip.setStripColor(PixelColor.PINK);
ledStrip.render();
delay(200);
}
}
public static void waitForKey(String context) {
System.out.println(context + ": Waiting...");
try {
new BufferedReader(new InputStreamReader(System.in)).readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
static void delay(long milliseconds) {
try {
Thread.sleep(milliseconds);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* Default Channel of the SPI Pins
*/
protected static final int DEFAULT_SPI_CHANNEL = 0;
/**
* Minimum time to wait for reset to occur in nanoseconds.
*/
private static final long LED_RESET_WAIT_TIME = 300_000;
/**
* The PI4J SPI
*/
protected final Spi spi;
/**
* The PI4J context
*/
protected final Context context;
/**
* The amount of all LEDs
*/
private final int numLeds;
/**
* Default frequency of a WS2812 Neopixel Strip
*/
private static final int FREQUENCY = 800_000;
/**
* between each rendering of the strip, there has to be a reset-time where nothing is written to the SPI
*/
private final long renderWaitTime;
/**
* The array of all pixels
*/
private final int[] leds;
/**
* The raw-data of all pixels, each int of LEDs is split into bits and converted to bytes to write
*/
private final byte[] pixelRaw;
/**
* the conversion from bit's of an integer to a byte we can write on the SPI
*/
private static final byte Bit_0 = (byte) 0b11000000;// 192 in Decimal
private static final byte Bit_1 = (byte) 0b11111000;// 248 in Decimal
private static final byte Bit_Reset = (byte) 0b00000000;// 0 in Decimal
/**
* Brightness value between 0 and 1
*/
private double brightness;
/**
* The time, when the last rendering happened
*/
private long lastRenderTime;
/**
* Creates a new simpleLed component with a custom BCM pin.
*
* @param pi4j
* Pi4J context
* @param numLeds
* How many LEDs are on this Strand
* @param brightness
* How bright the leds can be at max, Range 0 - 255
*/
public LedStripSimple(Context pi4j, int numLeds, double brightness) {
this(pi4j, numLeds, brightness, DEFAULT_SPI_CHANNEL);
}
/**
* Creates a new simpleLed component with a custom BCM pin.
*
* @param pi4j
* Pi4J context
* @param numLeds
* How many LEDs are on this Strand
* @param brightness
* How bright the leds can be at max, range 0 - 1
* @param channel
* which channel to use
*/
public LedStripSimple(Context pi4j, int numLeds, double brightness, int channel) {
if (numLeds < 1 || brightness < 0 || brightness > 1 || channel < 0 || channel > 1) {
throw new IllegalArgumentException("Illegal Constructor");
}
System.out.println("initialising a ledStrip with " + numLeds + " leds");
this.numLeds = numLeds;
this.leds = new int[numLeds];
this.brightness = brightness;
this.context = pi4j;
this.spi = pi4j.create(buildSpiConfig(pi4j, channel, FREQUENCY));
// The raw bytes that get sent to the ledStrip
// 3 Color channels per led, at 8 bytes each, with 2 reset bytes
pixelRaw = new byte[(3 * numLeds * 8) + 2];
// 1.25us per bit (1250ns)
renderWaitTime = numLeds * 3L * 8L * 1250L + LED_RESET_WAIT_TIME;
}
/**
* Builds a new SPI instance for the LED matrix
*
* @param pi4j
* Pi4J context
*
* @return SPI instance
*/
private SpiConfig buildSpiConfig(Context pi4j, int channel, int frequency) {
return Spi.newConfigBuilder(pi4j)
.id("SPI" + 1)
.name("LED Matrix")
.address(channel)
.mode(SpiMode.MODE_0)
.baud(8 * frequency) //bitbanging from Bit to SPI-Byte
.build();
}
/**
* Setting all LEDS off and closing the strip
*/
public void close() {
System.out.println("Turning all leds off before close");
allOff();
}
/**
* function to get the amount of the leds on the strip
*
* @return int with the amount of pixels
*/
public int getNumPixels() {
return numLeds;
}
/**
* function to get the color (as an int) of a specified led
*
* @param pixel
* which position on the ledStrip, range 0 - numLEDS-1
*
* @return the color of the specified led on the strip
*/
public int getPixelColor(int pixel) {
return leds[pixel];
}
/**
* setting the color of a specified led on the strip
*
* @param pixel
* which position on the strip, range 0 - numLEDS-1
* @param color
* the color that is set
*/
public void setPixelColor(int pixel, int color) {
leds[pixel] = color;
}
/**
* Setting all leds to the same color
*
* @param color
* the color that is set
*/
public void setStripColor(int color) {
Arrays.fill(leds, color);
}
/**
* Pixels are sent as follows: - The first transmitted pixel is the pixel closest to the transmitter. - The most
* significant bit is always sent first.
* <p>
* g7,g6,g5,g4,g3,g2,g1,g0,r7,r6,r5,r4,r3,r2,r1,r0,b7,b6,b5,b4,b3,b2,b1,b0
* \_____________________________________________________________________/ | _________________... | /
* __________________... | / / ___________________... | / / / GRB,GRB,GRB,GRB,...
*/
public void render() {
//beginning at 1, because the first byte is a reset
int counter = 1;
for (int i = 0; i < numLeds; i++) {
//Scaling the color to the max brightness
leds[i] = PixelColor.setRedComponent(leds[i], (int) (PixelColor.getRedComponent(leds[i]) * brightness));
leds[i] = PixelColor.setGreenComponent(leds[i], (int) (PixelColor.getGreenComponent(leds[i]) * brightness));
leds[i] = PixelColor.setBlueComponent(leds[i], (int) (PixelColor.getBlueComponent(leds[i]) * brightness));
// Calculating GRB from RGB
for (int j = 15; j >= 8; j--) {
if (((leds[i] >> j) & 1) == 1) {
pixelRaw[counter++] = Bit_1;
} else {
pixelRaw[counter++] = Bit_0;
}
}
for (int j = 23; j >= 16; j--) {
if (((leds[i] >> j) & 1) == 1) {
pixelRaw[counter++] = Bit_1;
} else {
pixelRaw[counter++] = Bit_0;
}
}
for (int j = 7; j >= 0; j--) {
if (((leds[i] >> j) & 1) == 1) {
pixelRaw[counter++] = Bit_1;
} else {
pixelRaw[counter++] = Bit_0;
}
}
}
// While bitbanging, the first and last byte have to be a reset
pixelRaw[0] = Bit_Reset;
pixelRaw[pixelRaw.length - 1] = Bit_Reset;
// waiting since last render time
long diff = Math.abs(System.nanoTime()) - lastRenderTime;
if (renderWaitTime - diff > 0) {
long wait = renderWaitTime - diff;
long millis = wait / 1_000_000L;
int nanos = (int) (wait % 1_000_000);
System.out.println("Waiting " + (millis) + "ms and " + nanos + "ns");
sleep(millis, nanos);
}
//writing on the PIN
spi.write(pixelRaw);
System.out.println("finished rendering");
lastRenderTime = Math.abs(System.nanoTime());
}
/**
* setting all LEDs off
*/
public void allOff() {
Arrays.fill(leds, 0);
render();
}
/**
* Utility function to sleep for the specified amount of milliseconds. An {@link InterruptedException} will be
* caught and ignored while setting the interrupt flag again.
*/
protected void sleep(long millis, int nanos) {
try {
Thread.sleep(millis, nanos);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* @return the current brightness
*/
public double getBrightness() {
return this.brightness;
}
/**
* Set the brightness of all LEDs
*
* @param brightness
* new max. brightness, range 0 - 1
*/
public void setBrightness(double brightness) {
if (brightness < 0 || brightness > 1) {
throw new IllegalArgumentException("Illegal Brightness Value. Must be between 0 and 1");
}
this.brightness = brightness;
}
public class PixelColor {
public static final int WHITE = 0xFFFFFF;
public static final int RED = 0xFF0000;
public static final int ORANGE = 0xFFA500;
public static final int YELLOW = 0xFFFF00;
public static final int GREEN = 0x00FF00;
public static final int LIGHT_BLUE = 0xadd8e6;
public static final int BLUE = 0x0000FF;
public static final int PURPLE = 0x800080;
public static final int PINK = 0xFFC0CB;
public static final int Color_COMPONENT_MAX = 0xff;
private static final int WHITE_MASK = 0xffffff;
private static final int RED_MASK = 0xff0000;
private static final int GREEN_MASK = 0x00ff00;
private static final int BLUE_MASK = 0x0000ff;
private static final int RED_OFF_MASK = 0x00ffff;
private static final int GREEN_OFF_MASK = 0xff00ff;
private static final int BLUE_OFF_MASK = 0xffff00;
/**
* validate if the color channel is in a valid range
*
* @param color
* the color which is to check
* @param value
* the color channel value
*/
public static void validateColorComponent(String color, int value) {
if (value < 0 || value >= 256) {
throw new IllegalArgumentException(
"Illegal Color value (" + value + ") for '" + color + "' - must be 0.." + Color_COMPONENT_MAX);
}
}
/**
* Get the red value of a color
*
* @param color
* provide the color
*
* @return the red value
*/
public static int getRedComponent(int color) {
return (color & RED_MASK) >> 16;
}
/**
* Set the red value of a color
*
* @param color
* provide the color
* @param red
* provide the desired red value
*
* @return the new color
*/
public static int setRedComponent(final int color, int red) {
validateColorComponent("Red", red);
int new_Color = color & RED_OFF_MASK;
new_Color |= red << 16;
return new_Color;
}
/**
* Get the green value of a color
*
* @param color
* provide the color
*
* @return the green value
*/
public static int getGreenComponent(int color) {
return (color & GREEN_MASK) >> 8;
}
/**
* Set the green value of a color
*
* @param color
* provide the color
* @param green
* provide the desired red value
*
* @return the new color
*/
public static int setGreenComponent(final int color, int green) {
validateColorComponent("Green", green);
int new_Color = color & GREEN_OFF_MASK;
new_Color |= green << 8;
return new_Color;
}
/**
* Get the blue value of a color
*
* @param color
* provide the color
*
* @return the blue value
*/
public static int getBlueComponent(int color) {
return color & BLUE_MASK;
}
/**
* Set the blue value of a color
*
* @param color
* provide the color
* @param blue
* provide the desired red value
*
* @return the new color
*/
public static int setBlueComponent(final int color, int blue) {
validateColorComponent("Blue", blue);
int new_Color = color & BLUE_OFF_MASK;
new_Color |= blue;
return new_Color;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment