+ * see Description on Pi4J website + */ public class Ads1115_App implements Application { @Override public void execute(Context pi4j) { - System.out.println("ADS1115 test started ..."); - //System.out.println("read all channels in single mode"); - //singleRead(pi4j); + System.out.println("ADS1115 demo started ..."); + + Ads1115 adc = new Ads1115(pi4j); + + System.out.println("read all channels in single mode"); + singleRead(adc); System.out.println("read all channels in continuous mode"); - continuousSlowRead(pi4j); + continuousRead(adc); - //System.out.println("read one channel in fast continuous mode"); - //continuousFastRead(pi4j); + //cleanup + adc.reset(); - System.out.println("ADS1115 test done"); + System.out.println("ADS1115 demo finished"); } - private void singleRead(Context pi4j) { - //start test + private void singleRead(Ads1115 adc) { System.out.println("Single read started ..."); - System.out.println("Create ADS1115 object"); - Ads1115 adc = new Ads1115(pi4j, 0x1, Ads1115.GAIN.GAIN_4_096V, Ads1115.ADDRESS.GND, 4); - //read analog value from all four channels - for (int i = 0; i < 30; i++) { - double aIn0 = adc.singleShotAIn0(); - double aIn1 = adc.singleShotAIn1(); - double aIn2 = adc.singleShotAIn2(); - double aIn3 = adc.singleShotAIn3(); - System.out.println("[" + i + "] Voltages: a0=" + String.format("%.3f", aIn0) + " V, a1=" + String.format("%.3f", aIn1) + " V, a2=" + String.format("%.3f", aIn2) + " V, a3=" + String.format("%.3f", aIn3) + " V"); - //wait for next read - delay(1000); - } - - pi4j.shutdown(); - - //end test + double aIn0 = adc.readValue(Ads1115.Channel.A0); + double aIn1 = adc.readValue(Ads1115.Channel.A1); + double aIn2 = adc.readValue(Ads1115.Channel.A2); + double aIn3 = adc.readValue(Ads1115.Channel.A3); + System.out.printf("Voltages: a0=%.3f V, a1=%.3f V, a2=%.3f V, a3=%.3f V%n", aIn0, aIn1, aIn2, aIn3); + System.out.println("Single read done."); } - private void continuousSlowRead(Context pi4j) { - //start test - System.out.println("Continuous slow read test started ..."); - - Ads1115 ads1115 = new Ads1115(pi4j, 0x01, Ads1115.GAIN.GAIN_4_096V, Ads1115.ADDRESS.GND, 4); + private void continuousRead(Ads1115 adc) { + System.out.println("Continuous read started ..."); // Register event handlers to print a message on value change - ads1115.setConsumerSlowReadChannel0((value) -> System.out.println("The actual value from channel 0 is: " + String.format("%.3f", value) + "voltage.")); - ads1115.setConsumerSlowReadChannel1((value) -> System.out.println("The actual value from channel 1 is: " + String.format("%.3f", value) + "voltage.")); - ads1115.setConsumerSlowReadChannel2((value) -> System.out.println("The actual value from channel 2 is: " + String.format("%.3f", value) + "voltage.")); - ads1115.setConsumerSlowReadChannel3((value) -> System.out.println("The actual value from channel 3 is: " + String.format("%.3f", value) + "voltage.")); - - //start continuous measuring - ads1115.startSlowContinuousReadingAllChannels(0.1, 10); + adc.onValueChange(Ads1115.Channel.A0, (value) -> System.out.printf("Value channel 0 : %.2f V%n", value)); + adc.onValueChange(Ads1115.Channel.A1, (value) -> System.out.printf("Value channel 1 : %.2f V%n", value)); + adc.onValueChange(Ads1115.Channel.A2, (value) -> System.out.printf("Value channel 2 : %.2f V%n", value)); + adc.onValueChange(Ads1115.Channel.A3, (value) -> System.out.printf("Value channel 3 : %.2f V%n", value)); - // Wait while handling events before exiting - delay(30000); + adc.startContinuousReading(0.1); - //stop continuous measuring - ads1115.stopSlowReadContinuousReadingAllChannels(); + // continue reading for 30 seconds + delay(Duration.ofSeconds(30)); - //deregister all handlers - ads1115.deregisterAll(); + adc.stopContinuousReading(); - pi4j.shutdown(); - - //end test - System.out.println("Continuous slow read test done."); + System.out.println("Continuous read done."); } - private void continuousFastRead(Context pi4j) { - //start test - System.out.println("Continuous fast read test started ..."); - - Ads1115 ads1115 = new Ads1115(pi4j, 0x1, Ads1115.GAIN.GAIN_4_096V, Ads1115.ADDRESS.GND, 4); - - // Register event handlers to print a message on value change - ads1115.setConsumerFastRead((value) -> { - System.out.println("The actual value from fast read channel is: " + String.format("%.3f", value) + "voltage."); - }); - - for (int i = 0; i < 4; i++) { - //start continuous measuring - ads1115.startFastContinuousReading(i, 0.1, 10); - - // Wait while handling events before exiting - delay(20000); - - //stop continuous measuring - ads1115.stopFastContinuousReading(); - } - - - //deregister all handlers - ads1115.deregisterAll(); - - pi4j.shutdown(); - - //end test - System.out.println("Continuous fast read test done."); - } } diff --git a/src/main/java/com/pi4j/catalog/applications/Buzzer_App.java b/src/main/java/com/pi4j/catalog/applications/Buzzer_App.java index 9e1680c..ab54c91 100644 --- a/src/main/java/com/pi4j/catalog/applications/Buzzer_App.java +++ b/src/main/java/com/pi4j/catalog/applications/Buzzer_App.java @@ -1,53 +1,79 @@ package com.pi4j.catalog.applications; +import java.time.Duration; +import java.util.List; + import com.pi4j.context.Context; + import com.pi4j.catalog.Application; +import com.pi4j.catalog.components.base.PIN; import com.pi4j.catalog.components.Buzzer; -import com.pi4j.catalog.components.helpers.PIN; + +import static com.pi4j.catalog.components.Buzzer.Note.*; /** - * This example shows how to use the buzzer component by playing different tunes on it + * The buzzer component is an example of an actuator making use of a PWM-channel to play different tones or even simple melodies. + *
+ * see Description on Pi4J website
*/
public class Buzzer_App implements Application {
+
+ //this is how you compose a simple melody
+ //Piano baseline of Seven Nation Army by the white Stripes
+ private final List
+ * see Description on Pi4J website
+ */
public class Camera_App implements Application {
@Override
public void execute(Context pi4j) {
- System.out.println("Initializing the camera");
+ System.out.println("Camera demo started");
+
Camera camera = new Camera();
System.out.println("Taking a default picture");
- camera.takeStill();
+ camera.recordPicture();
System.out.println("Taking a pic with different parameters");
- var config = Camera.PicConfig.Builder.newInstance()
+ var config = Camera.newPictureConfigBuilder()
.outputPath("/home/pi/Pictures/")
.delay(3000)
.disablePreview(true)
@@ -25,17 +33,20 @@ public void execute(Context pi4j) {
.height(800)
.build();
- camera.takeStill(config);
+ camera.recordPicture(config);
System.out.println("Waiting for camera to take pic");
- delay(4000);
+ delay(Duration.ofSeconds(4));
System.out.println("Taking a video for 3 seconds");
- var vidconfig = Camera.VidConfig.Builder.newInstance()
+ var vidconfig = Camera.newVidConfigBuilder()
.outputPath("/home/pi/Videos/")
.recordTime(3000)
.useDate(true)
.build();
- camera.takeVid(vidconfig);
+ camera.recordVideo(vidconfig);
+
+ camera.reset();
+ System.out.println("Camera demo finished");
}
}
diff --git a/src/main/java/com/pi4j/catalog/applications/JoystickAnalog_App.java b/src/main/java/com/pi4j/catalog/applications/JoystickAnalog_App.java
index bcae893..d1db341 100644
--- a/src/main/java/com/pi4j/catalog/applications/JoystickAnalog_App.java
+++ b/src/main/java/com/pi4j/catalog/applications/JoystickAnalog_App.java
@@ -1,54 +1,49 @@
package com.pi4j.catalog.applications;
+import java.time.Duration;
+
import com.pi4j.context.Context;
+
import com.pi4j.catalog.Application;
+import com.pi4j.catalog.components.base.PIN;
import com.pi4j.catalog.components.Ads1115;
import com.pi4j.catalog.components.JoystickAnalog;
-import com.pi4j.catalog.components.helpers.PIN;
/**
- * This example shows how to use the analog joystick component by registering different interactions for the positions of the joystick
+ * This example shows how to use the analog joystick component
+ *
+ * see Description on Pi4J website
*/
public class JoystickAnalog_App implements Application {
@Override
public void execute(Context pi4j) {
- System.out.println("Joystick test started ...");
+ System.out.println("Joystick demo started ...");
- Ads1115 ads1115 = new Ads1115(pi4j, 0x01, Ads1115.GAIN.GAIN_4_096V, Ads1115.ADDRESS.GND, 4);
-
- //joystick with normalized axis from 0 to 1
- JoystickAnalog joystick = new JoystickAnalog(pi4j, ads1115, 0, 1, 3.3, true, PIN.D26);
+ // an analog joystick needs an ADC
+ Ads1115 ads1115 = new Ads1115(pi4j);
//joystick with normalized axis from -1 to 1
- //JoystickAnalog joystick = new JoystickAnalog(pi4j, ads1115, 0, 1, 3.3, false, PIN.D26);
-
- //register event handlers
- joystick.xOnMove((value) -> System.out.println("Current value of joystick x axis is: " + String.format("%.3f", value)));
- joystick.yOnMove((value) -> System.out.println("Current value of joystick y axis is: " + String.format("%.3f", value)));
+ JoystickAnalog joystick = new JoystickAnalog(ads1115, Ads1115.Channel.A0, Ads1115.Channel.A1, PIN.D26, true);
- joystick.pushOnDown(() -> System.out.println("Pressing the Button"));
- joystick.pushOnUp(() -> System.out.println("Stopped pressing."));
- joystick.pushWhilePressed(() -> System.out.println("Button is still pressed."), 1000);
+ //register all event handlers you need
+ joystick.onMove((xPos, yPos) -> System.out.printf("Current position of joystick is: %.2f, %.2f%n", xPos, yPos),
+ () -> System.out.println("Joystick in home position"));
- joystick.calibrateJoystick();
+ joystick.onDown (() -> System.out.println("Pressing the button"));
+ joystick.onUp (() -> System.out.println("Stopped pressing."));
+ joystick.whilePressed(() -> System.out.println("Button is still pressed."), Duration.ofMillis(500));
- //start continuous reading with single shot in this mode you can connect up to 4 devices to the analog module
- joystick.start(0.05, 10);
+ //start continuous reading after all ADC channels are configured
+ ads1115.startContinuousReading(0.1);
- //wait while handling events before exiting
System.out.println("Move the joystick to see it in action!");
- delay(30_000);
-
- //stop continuous reading
- joystick.stop();
-
- delay(1000
- );
+ //wait while handling events before exiting
+ delay(Duration.ofSeconds(30));
- //deregister all event handlers
- joystick.deregisterAll();
+ //cleanup
+ joystick.reset();
- System.out.println("Joystick test done");
+ System.out.println("Joystick demo finished");
}
}
diff --git a/src/main/java/com/pi4j/catalog/applications/Joystick_App.java b/src/main/java/com/pi4j/catalog/applications/Joystick_App.java
index d55b40f..d717875 100644
--- a/src/main/java/com/pi4j/catalog/applications/Joystick_App.java
+++ b/src/main/java/com/pi4j/catalog/applications/Joystick_App.java
@@ -1,43 +1,52 @@
package com.pi4j.catalog.applications;
+import java.time.Duration;
+
import com.pi4j.context.Context;
+
import com.pi4j.catalog.Application;
-import com.pi4j.catalog.components.helpers.PIN;
+import com.pi4j.catalog.components.base.PIN;
import com.pi4j.catalog.components.Joystick;
/**
* This example shows how to use the Joystick by registering actions for each position of the joystick
+ *
+ * see Description on Pi4J website
*/
public class Joystick_App implements Application {
@Override
public void execute(Context pi4j) {
+ System.out.println("Joystick demo started ...");
- System.out.println("Joystick app started ...");
final var joystick = new Joystick(pi4j, PIN.D5, PIN.D6, PIN.PWM13, PIN.PWM19, PIN.D26);
- //Register event handlers to print a message when pressed (onDown) and (onUp)
- joystick.onNorth(() -> System.out.println("Start Pressing joystick button North"));
- joystick.whileNorth(1000, () -> System.out.println("Pressing joystick button North"));
+ //Register all event handlers
+ joystick.onNorth(() -> System.out.println("Start NORTH"));
+ joystick.whileNorth(() -> System.out.println("Still NORTH"),
+ Duration.ofSeconds(1));
- joystick.onWest(() -> System.out.println("Start Pressing joystick button West"));
- joystick.whileWest(1000, () -> System.out.println("Pressing joystick button West"));
+ joystick.onWest(() -> System.out.println("Start WEST"));
+ joystick.whileWest(() -> System.out.println("Still WEST"),
+ Duration.ofSeconds(1));
- joystick.onSouth(() -> System.out.println("Start Pressing joystick button South"));
- joystick.whileSouth(1000, () -> System.out.println("Pressing joystick button South"));
+ joystick.onSouth(() -> System.out.println("Start SOUTH"));
+ joystick.whileSouth(() -> System.out.println("Still SOUTH"),
+ Duration.ofSeconds(1));
- joystick.onEast(() -> System.out.println(" Start Pressing joystick button East"));
- joystick.whileEast(1000, () -> System.out.println("Pressing joystick button East"));
+ joystick.onEast(() -> System.out.println(" Start EAST"));
+ joystick.whileEast(() -> System.out.println("Still EAST"),
+ Duration.ofSeconds(1));
- joystick.onPushDown(() -> System.out.println("Start Pressing joystick button PUSH"));
- joystick.onPushUp(() -> System.out.println("Stop pressing joystick button PUSH"));
+ joystick.onPushDown(() -> System.out.println("Start PUSH"));
+ joystick.onPushUp(() -> System.out.println("Still PUSHing"));
// Wait for 15 seconds while handling events before exiting
- System.out.println("Press the button to see it in action!");
- delay(15_000);
+ System.out.println("Move the joystick and push it's button to see it in action!");
+ delay(Duration.ofSeconds(15));
- // Unregister all event handlers to exit this application in a clean way
- joystick.deRegisterAll();
+ // cleanup
+ joystick.reset();
- System.out.println("Joystick app done.");
+ System.out.println("Joystick demo finished.");
}
}
diff --git a/src/main/java/com/pi4j/catalog/applications/LcdDisplay_App.java b/src/main/java/com/pi4j/catalog/applications/LcdDisplay_App.java
index 19e92ae..d2d21fb 100644
--- a/src/main/java/com/pi4j/catalog/applications/LcdDisplay_App.java
+++ b/src/main/java/com/pi4j/catalog/applications/LcdDisplay_App.java
@@ -1,67 +1,81 @@
package com.pi4j.catalog.applications;
+import java.time.Duration;
+
import com.pi4j.context.Context;
+
import com.pi4j.catalog.Application;
import com.pi4j.catalog.components.LcdDisplay;
/**
* This example shows how to use the LCDDisplay component by writing different things to the display
+ *
+ * see Description on Pi4J website
*/
public class LcdDisplay_App implements Application {
@Override
public void execute(Context pi4j) {
- //Create a Component, with amount of ROWS and COLUMNS of the Device
- LcdDisplay lcd = new LcdDisplay(pi4j, 4, 20);
- System.out.println("Here we go.. let's have some fun with that LCD Display!");
+ System.out.println("LCD demo started");
- // Turn on the backlight makes the display appear turned on
- lcd.setDisplayBacklight(true);
+ //Create a Component, with amount of ROWS and COLUMNS of the device
+ //LcdDisplay lcd = new LcdDisplay(pi4j); //2x16 is default
+ LcdDisplay lcd = new LcdDisplay(pi4j, 4, 20);
- // Write text to the lines separate
- lcd.displayText("Hello", 1);
- lcd.displayText(" World!", 2);
- //A display with just 2 lines would fail by here
- lcd.displayText("Line 3", 3);
- lcd.displayText(" Line 4", 4);
+ // Write text to specific position
+ lcd.displayLineOfText("Hello" , 0);
+ lcd.displayLineOfText("World!", 1, 3);
// Wait a little to have some time to read it
- delay(3000);
+ delay(Duration.ofSeconds(3));
- // Clear the display to start next parts
lcd.clearDisplay();
+ lcd.centerTextInLine("Hi", 0);
+
+ delay(Duration.ofSeconds(1));
+
// To write some text there are different methods. The simplest one is this one which automatically inserts
// linebreaks if needed.
lcd.displayText("Boohoo that's so simple to use!");
+ delay(Duration.ofSeconds(3));
- // Delay again
- delay(3000);
-
- // Of course, it is also possible to write with newLine Chars
- lcd.displayText("Some big text \nwith some new Lines \n just for testing");
- delay(3000);
+ // Of course, it is also possible to use linebreaks
+ lcd.displayText("Some big text \nwith a new line\nonly displayed on 4 row LCD");
+ delay(Duration.ofSeconds(4));
- // Of course, it is also possible to write long text
+ // Long text are cut to the bone
lcd.displayText("Some big text with no new lines, just to test how many lines will get filled");
- delay(3000);
+ delay(Duration.ofSeconds(4));
// Clear the display to start next parts
lcd.clearDisplay();
- // Let's try to draw a house. To keep this method short and clean we create the characters in a separate
- // method below.
+ // Let's try to draw a house.
+ // To keep this method short and clean we create the characters in a separate method below.
createCharacters(lcd);
// Now all characters are ready. Just draw them on the right place by moving the cursor and writing the
// created characters to specific positions
- lcd.writeCharacter('\1', 1, 1);
- lcd.writeCharacter('\2', 2, 1);
- lcd.writeCharacter('\3', 1, 2);
- lcd.writeCharacter('\4', 2, 2);
- delay(3000);
-
- // Turn off the backlight makes the display appear turned off
- lcd.setDisplayBacklight(false);
+ lcd.writeCharacter('\1', 0, 1);
+ lcd.writeCharacter('\2', 0, 2);
+ lcd.writeCharacter('\3', 1, 1);
+ lcd.writeCharacter('\4', 1, 2);
+
+ delay(Duration.ofSeconds(3));
+
+ // we've built a rolling home
+ for (int i = 0; i < 5; i++) {
+ lcd.scrollRight();
+ delay(Duration.ofSeconds(1));
+ }
+
+ for (int i = 0; i < 5; i++) {
+ lcd.scrollLeft();
+ delay(Duration.ofSeconds(1));
+ }
+
+ lcd.reset();
+ System.out.println("LCD demo finished");
}
public void createCharacters(LcdDisplay lcd) {
diff --git a/src/main/java/com/pi4j/catalog/applications/LedButton_App.java b/src/main/java/com/pi4j/catalog/applications/LedButton_App.java
index 7129ae0..8ebd552 100644
--- a/src/main/java/com/pi4j/catalog/applications/LedButton_App.java
+++ b/src/main/java/com/pi4j/catalog/applications/LedButton_App.java
@@ -1,44 +1,43 @@
package com.pi4j.catalog.applications;
+import java.time.Duration;
+
import com.pi4j.context.Context;
+
import com.pi4j.catalog.Application;
-import com.pi4j.catalog.components.helpers.PIN;
+import com.pi4j.catalog.components.base.PIN;
import com.pi4j.catalog.components.LedButton;
/**
* This example shows how to use the LEDButton component by registering actions for the interaction with the button, while simultaneously toggling the LED
+ *
+ * see Description on Pi4J website
*/
public class LedButton_App implements Application {
@Override
public void execute(Context pi4j) {
- System.out.println("LED button app started ...");
+ System.out.println("LED button demo started ...");
// Initialize the button component
- final LedButton ledButton = new LedButton(pi4j, PIN.D26, Boolean.FALSE, PIN.PWM19);
+ final LedButton ledButton = new LedButton(pi4j, PIN.D26, false, PIN.D5);
- // Turn on the LED to have a defined state
- ledButton.ledOn();
- //see the LED for a Second
- delay(1000);
+ // Make a flashing light by toggling the LED
+ for (int i = 0; i < 4; i++) {
+ ledButton.toggleLed();
+ delay(Duration.ofMillis(500));
+ }
- // Register event handlers to print a message when pressed (onDown) and depressed (onUp)
- ledButton.btnOnDown(() -> System.out.println("Pressing the Button"));
- ledButton.btnOnUp(() -> System.out.println("Stopped pressing."));
+ // Register event handlers to turn LED on when pressed (onDown) and off when depressed (onUp)
+ ledButton.onDown(() -> ledButton.ledOn());
+ ledButton.onUp (() -> ledButton.ledOff());
// Wait for 15 seconds while handling events before exiting
System.out.println("Press the button to see it in action!");
-
- // Make a flashing light by toggling the LED every second
- // in the meantime, the Button can still be pressed, as we only freeze the main thread
- for (int i = 0; i < 15; i++) {
- System.out.println(ledButton.ledToggleState());
- delay(1000);
- }
+ delay(Duration.ofSeconds(15));
// Unregister all event handlers to exit this application in a clean way
- ledButton.btnDeRegisterAll();
- ledButton.ledOff();
+ ledButton.reset();
- System.out.println("LED button app done.");
+ System.out.println("LED button demo finished.");
}
}
\ No newline at end of file
diff --git a/src/main/java/com/pi4j/catalog/applications/LedMatrix_App.java b/src/main/java/com/pi4j/catalog/applications/LedMatrix_App.java
index 750e121..b22e429 100644
--- a/src/main/java/com/pi4j/catalog/applications/LedMatrix_App.java
+++ b/src/main/java/com/pi4j/catalog/applications/LedMatrix_App.java
@@ -1,41 +1,55 @@
package com.pi4j.catalog.applications;
+import java.time.Duration;
+
import com.pi4j.context.Context;
+
import com.pi4j.catalog.Application;
import com.pi4j.catalog.components.LedMatrix;
import com.pi4j.catalog.components.LedStrip;
/**
* This example shows how to use the LEDMatrix component by setting the LEDs on the strips to different colors
+ *
+ * see Description on Pi4J website
*/
public class LedMatrix_App implements Application {
@Override
public void execute(Context pi4j) {
- System.out.println("LED matrix app started ...");
- int Rows = 3;
- int Columns = 4;
- double brightness = 0.5;
+ System.out.println("LED matrix demo started ...");
+ int rows = 2;
+ int columns = 5;
System.out.println("Initialising the matrix");
- LedMatrix ledMatrix = new LedMatrix(pi4j, Rows, Columns, brightness);
+ LedMatrix ledMatrix = new LedMatrix(pi4j, rows, columns);
+
+ System.out.println("whole matrix Red.");
+ ledMatrix.setMatrixColor(LedStrip.LedColor.RED);
+ ledMatrix.render(Duration.ofSeconds(3));
+
+ System.out.println("First row is GREEN");
+ ledMatrix.allOff();
+ ledMatrix.setRowColor(0, LedStrip.LedColor.GREEN);
+ ledMatrix.render(Duration.ofSeconds(3));
- System.out.println("Setting all LEDs to Red.");
- ledMatrix.setMatrixColor(LedStrip.PixelColor.RED);
- ledMatrix.render();
- delay(3000);
+ System.out.println("Second row is GREEN");
+ ledMatrix.allOff();
+ ledMatrix.setRowColor(1, LedStrip.LedColor.GREEN);
+ ledMatrix.render(Duration.ofSeconds(3));
- System.out.println("setting the second strip to green");
- ledMatrix.setStripColor(1, LedStrip.PixelColor.GREEN);
- ledMatrix.render();
- delay(3000);
+ System.out.println("Second column is BLUE");
+ ledMatrix.allOff();
+ ledMatrix.setColumnColor(1, LedStrip.LedColor.BLUE);
+ ledMatrix.render(Duration.ofSeconds(3));
- System.out.println("Setting the third led of the third strip to Yellow");
- ledMatrix.setPixelColor(2, 2, LedStrip.PixelColor.YELLOW);
- ledMatrix.render();
- delay(3000);
+ System.out.println("Third led of the first and second row is YELLOW");
+ ledMatrix.allOff();
+ ledMatrix.setPixelColor(0, 2, LedStrip.LedColor.YELLOW);
+ ledMatrix.setPixelColor(1, 2, LedStrip.LedColor.YELLOW);
+ ledMatrix.render(Duration.ofSeconds(3));
- ledMatrix.close();
+ ledMatrix.reset();
- System.out.println("LED matrix app done.");
+ System.out.println("LED matrix app finished.");
}
}
diff --git a/src/main/java/com/pi4j/catalog/applications/LedStrip_App.java b/src/main/java/com/pi4j/catalog/applications/LedStrip_App.java
index 486fd66..44d8a96 100644
--- a/src/main/java/com/pi4j/catalog/applications/LedStrip_App.java
+++ b/src/main/java/com/pi4j/catalog/applications/LedStrip_App.java
@@ -1,51 +1,71 @@
package com.pi4j.catalog.applications;
+import java.time.Duration;
+
import com.pi4j.context.Context;
+
import com.pi4j.catalog.Application;
import com.pi4j.catalog.components.LedStrip;
/**
- * This example shows how to use the LEDStrip component by setting the LEDs on the strip to different colors
+ * This example shows how to use the LEDStrip component by setting the LEDs on the strip to different colors.
+ *
+ * see Description on Pi4J website
*/
public class LedStrip_App implements Application {
@Override
public void execute(Context pi4j) {
System.out.println("LED strip app started ...");
- // Initialize the RGB
- int pixels = 4;
- final LedStrip ledStrip = new LedStrip(pi4j, pixels, 0.5);
- //set them all off, so nothing is shining
- System.out.println("Starting with setting all leds off");
+ // Initialize the strip
+ Duration ms50 = Duration.ofMillis(50);
+ Duration ms500 = Duration.ofMillis(500);
+ Duration sec1 = Duration.ofSeconds(1);
+
+ int leds = 25; //
+ final LedStrip ledStrip = new LedStrip(pi4j, leds);
+
+ delay(sec1);
+
+ System.out.println("LED strip shines purple");
+ ledStrip.setStripColor(LedStrip.LedColor.PURPLE);
+ ledStrip.render(sec1);
+
+ System.out.println("turn strip off");
ledStrip.allOff();
+ ledStrip.render(sec1);
+
+ System.out.println("toggle between GREEN and RED");
+ ledStrip.alternate(LedStrip.LedColor.GREEN, LedStrip.LedColor.RED, ms500, 3);
- System.out.println("setting the LEDs to RED");
- ledStrip.setStripColor(LedStrip.PixelColor.RED);
- ledStrip.render();
- delay(3000);
+ System.out.println("setting the LEDs to blue and the first one to purple");
+ ledStrip.setStripColor(LedStrip.LedColor.BLUE);
+ ledStrip.setPixelColor(0, LedStrip.LedColor.PURPLE);
+ ledStrip.render(ms500);
- System.out.println("setting the LEDs to Light Blue");
- ledStrip.setStripColor(LedStrip.PixelColor.LIGHT_BLUE);
- ledStrip.render();
- delay(3000);
+ System.out.println("Start a kind of animation");
+ for(int i = 0; i < leds -1; i++){
+ ledStrip.setPixelColor(i, LedStrip.LedColor.BLUE);
+ ledStrip.setPixelColor(i+1, LedStrip.LedColor.PURPLE);
+ ledStrip.render(ms50);
+ }
- System.out.println("setting the first led to Purple");
- ledStrip.setPixelColor(0, LedStrip.PixelColor.PURPLE);
- ledStrip.render();
- delay(3000);
+ for(int i = leds -1; i > 0; i--){
+ ledStrip.setPixelColor(i, LedStrip.LedColor.BLUE);
+ ledStrip.setPixelColor(i - 1, LedStrip.LedColor.PURPLE);
+ ledStrip.render(ms50);
+ }
+ delay(ms500);
- System.out.println("setting the brightness to full and just show the first led as White");
+ System.out.println("setting the brightness to full and show the first LED as white");
ledStrip.allOff();
- ledStrip.setBrightness(1);
- ledStrip.setPixelColor(0, LedStrip.PixelColor.WHITE);
- ledStrip.render();
- delay(3000);
+ ledStrip.setMaxBrightness(1);
+ ledStrip.setPixelColor(0, LedStrip.LedColor.WHITE);
+ ledStrip.render(Duration.ofSeconds(2));
//finishing and closing
- ledStrip.close();
- System.out.println("closing the app");
- System.out.println("Color "+ ledStrip.getPixelColor(0));
+ ledStrip.reset();
- System.out.println("LED strip app done.");
+ System.out.println("LED strip demo finished.");
}
}
\ No newline at end of file
diff --git a/src/main/java/com/pi4j/catalog/applications/Potentiometer_App.java b/src/main/java/com/pi4j/catalog/applications/Potentiometer_App.java
index b67aba6..2238bf9 100644
--- a/src/main/java/com/pi4j/catalog/applications/Potentiometer_App.java
+++ b/src/main/java/com/pi4j/catalog/applications/Potentiometer_App.java
@@ -1,41 +1,54 @@
package com.pi4j.catalog.applications;
+import java.time.Duration;
+
import com.pi4j.context.Context;
+
import com.pi4j.catalog.Application;
import com.pi4j.catalog.components.Ads1115;
import com.pi4j.catalog.components.Potentiometer;
/**
* This example shows how to use the potentiometer component displaying the values of the hooked potentiometer
+ *
+ * see Description on Pi4J website
*/
public class Potentiometer_App implements Application {
@Override
public void execute(Context pi4j) {
- System.out.println("Potentiometer test started ...");
+ System.out.println("Potentiometer demo started ...");
- Ads1115 ads1115 = new Ads1115(pi4j, 0x01, Ads1115.GAIN.GAIN_4_096V, Ads1115.ADDRESS.GND, 4);
+ // a potentiometer needs an ADC
+ Ads1115 ads1115 = new Ads1115(pi4j);
- Potentiometer poti = new Potentiometer(ads1115, 0, 3.3);
+ Potentiometer poti = new Potentiometer(ads1115, Ads1115.Channel.A0);
//read current value from poti one time
- System.out.println("Current value of the poti is " + String.format("%.3f", poti.singleShotGetVoltage()) + " voltage.");
+ System.out.printf("P0 raw value is %.2f V%n", poti.readCurrentVoltage());
//read current value from the poti in percent one time
- System.out.println("The potentiometer slider is currently at " + String.format("%.3f", poti.singleShotGetNormalizedValue()) + " % of its full travel.");
+ System.out.printf("P0 normalized value is %.2f %%%n", poti.readNormalizedValue());
// Register event handlers to print a message when potentiometer is moved
- poti.setConsumerSlowReadChan((value) -> System.out.println("The potentiometer slider is currently at " + String.format("%.3f", value) + " % of its full travel."));
+ poti.onNormalizedValueChange((value) -> System.out.printf("P0 slider is at %.2f %%%n", value));
- //start continuous reading with single shot in this mode you can connect up to 4 devices to the analog module
- poti.startSlowContinuousReading(0.05, 10);
+ //you can attach a second potentiometer to another channel, if you like:
+// Potentiometer potiWithCenterPosition = new Potentiometer(ads1115, Ads1115.Channel.A1, Potentiometer.Range.MINUS_ONE_TO_ONE);
+// potiWithCenterPosition.onNormalizedValueChange((value) -> System.out.printf("P1 slider is at %.2f %%", value)));
+
+ //you have to start continuous reading on ADC (because you can use up to 4 channels and all of them need to be fully configured before starting to read the values)
+ ads1115.startContinuousReading(0.1);
- // Wait while handling events before exiting
System.out.println("Move the potentiometer to see it in action!");
- delay(30_000);
+ // Wait while handling events before exiting
+ delay(Duration.ofSeconds(15));
+
+ ads1115.stopContinuousReading();
- //stop continuous reading
- poti.stopSlowContinuousReading();
+ System.out.println("No new values should be reported");
+ delay(Duration.ofSeconds(5));
- System.out.println("Potentiometer test done");
+ ads1115.reset();
+ System.out.println("Potentiometer demo finished");
}
}
diff --git a/src/main/java/com/pi4j/catalog/applications/SerialGps_App.java b/src/main/java/com/pi4j/catalog/applications/SerialGps_App.java
index 439299f..267fa65 100644
--- a/src/main/java/com/pi4j/catalog/applications/SerialGps_App.java
+++ b/src/main/java/com/pi4j/catalog/applications/SerialGps_App.java
@@ -1,76 +1,28 @@
package com.pi4j.catalog.applications;
-import com.pi4j.catalog.Application;
-import com.pi4j.catalog.components.SerialReader;
-import com.pi4j.catalog.components.helpers.PrintInfo;
+import java.time.Duration;
+
import com.pi4j.context.Context;
-import com.pi4j.io.serial.FlowControl;
-import com.pi4j.io.serial.Parity;
-import com.pi4j.io.serial.Serial;
-import com.pi4j.io.serial.StopBits;
-import com.pi4j.util.Console;
+
+import com.pi4j.catalog.Application;
+import com.pi4j.catalog.components.SerialGps;
public class SerialGps_App implements Application {
@Override
public void execute(Context pi4j) {
+ System.out.println("GPS demo started");
- // Create Pi4J console wrapper/helper
- // (This is a utility class to abstract some boilerplate stdin/stdout code)
- var console = new Console();
-
- // Print program title/header
- console.title("<-- The Pi4J Project -->", "Serial Example project");
-
- // ------------------------------------------------------------
- // Output Pi4J Context information
- // ------------------------------------------------------------
- // The created Pi4J Context initializes platforms, providers
- // and the I/O registry. To help you to better understand this
- // approach, we print out the info of these. This can be removed
- // from your own application.
- // OPTIONAL
- PrintInfo.printLoadedPlatforms(console, pi4j);
- PrintInfo.printDefaultPlatform(console, pi4j);
- PrintInfo.printProviders(console, pi4j);
-
- // Here we will create I/O interface for the serial communication.
- Serial serial = pi4j.create(Serial.newConfigBuilder(pi4j)
- .use_9600_N81()
- .dataBits_8()
- .parity(Parity.NONE)
- .stopBits(StopBits._1)
- .flowControl(FlowControl.NONE)
- .id("my-serial")
- .device("/dev/ttyS0")
- .provider("pi-gpio-serial")
- .build());
- serial.open();
-
- // Wait till the serial port is open
- console.print("Waiting till serial port is open");
- while (!serial.isOpen()) {
- console.print(".");
- delay(250);
- }
- console.println("");
- console.println("Serial port is open");
-
- // OPTIONAL: print the registry
- PrintInfo.printRegistry(console, pi4j);
-
- // Start a thread to handle the incoming data from the serial port
- SerialReader serialReader = new SerialReader(console, serial);
- Thread serialReaderThread = new Thread(serialReader, "SerialReader");
- serialReaderThread.setDaemon(true);
- serialReaderThread.start();
+ SerialGps gps = new SerialGps(pi4j,
+ (pos) -> System.out.printf("Position: %.6f, %.6f; DMS: %s%n", pos.latitude(), pos.longitude(), pos.dms() ),
+ (alt) -> System.out.printf("Altitude: %.1fm%n", alt));
- while (serial.isOpen()) {
- delay(500);
- }
+ gps.start();
- serialReader.stopReading();
+ //provide positions for 15 sec
+ delay(Duration.ofSeconds(15));
- console.println("Serial is no longer open");
+ gps.reset();
+ System.out.println("GPS demo finished");
}
}
diff --git a/src/main/java/com/pi4j/catalog/applications/Servo_App.java b/src/main/java/com/pi4j/catalog/applications/Servo_App.java
index 8a7599c..7c1e48a 100644
--- a/src/main/java/com/pi4j/catalog/applications/Servo_App.java
+++ b/src/main/java/com/pi4j/catalog/applications/Servo_App.java
@@ -1,58 +1,60 @@
package com.pi4j.catalog.applications;
+import java.time.Duration;
+
import com.pi4j.context.Context;
+
import com.pi4j.catalog.Application;
+import com.pi4j.catalog.components.base.PIN;
import com.pi4j.catalog.components.ServoMotor;
-import com.pi4j.catalog.components.helpers.PIN;
/**
* This example shows how to use the servo component by turning the servo to different positions
+ *
+ * see Description on Pi4J website
*/
public class Servo_App implements Application {
@Override
public void execute(Context pi4j) {
+ System.out.println("Server demo started ...");
// Initialize servo motor component
- final var servoMotor = new ServoMotor(pi4j, PIN.PWM18);
+ final var servoMotor = new ServoMotor(pi4j, PIN.PWM18, 50, -90.0f, 90.0f, 2.0f, 12f);
- // Demonstrate the percentage mapping on the servo
+ // Demonstrate the percentage mapping on the servo
System.out.println("In 2 seconds, the servo motor will move to the left-most position which is 0%");
- delay(2000);
- servoMotor.setPercent(10);
+ delay(Duration.ofSeconds(2));
+ servoMotor.setPercent(0);
System.out.println("In another 2 seconds, the servo motor will show 100% by moving to the right-most position");
- delay(2000);
- servoMotor.setPercent(90);
+ delay(Duration.ofSeconds(2));
+ servoMotor.setPercent(100);
System.out.println("Last but not least, in 2 more seconds the servo will be centered to display 50%");
- delay(2000);
+ delay(Duration.ofSeconds(2));
servoMotor.setPercent(50);
+
// Sweep once from left to right using the setAngle function
System.out.println("We will sweep once to the left in 2 seconds...");
- delay(2000);
- servoMotor.setAngle(-80);
+ delay(Duration.ofSeconds(2));
+ servoMotor.setAngle(-45);
System.out.println("... and now to the right in 2 more seconds!");
- delay(2000);
- servoMotor.setAngle(80);
+ delay(Duration.ofSeconds(2));
+ servoMotor.setAngle(45);
// Use a custom range for displaying the data
System.out.println("Imagine a pointer on the servo positioned above a label between -20ºC and +40ºC");
System.out.println("By using the setRange() method, we can automatically map our temperature range to the servo range!");
System.out.println("As an example, in five seconds the servo will show -10º which should be on the far left of the servo.");
- delay(2000);
+ delay(Duration.ofSeconds(2));
servoMotor.setRange(-20, +40); // This will define our range as values between -20 and +40
servoMotor.moveOnRange(-10); // This will map -10 based on the previously defined range
- delay(2000);
-
- //back to middle position
- System.out.println("To finish the servo will be centered to display 50%");
- servoMotor.setPercent(50);
+ servoMotor.reset();
- // And this demo is over, sleep for a second to give the servo some time to position itself
- delay(1000);
+ System.out.println("Server demo finished");
}
}
diff --git a/src/main/java/com/pi4j/catalog/applications/SimpleButton_App.java b/src/main/java/com/pi4j/catalog/applications/SimpleButton_App.java
index f60be70..1d89d5c 100644
--- a/src/main/java/com/pi4j/catalog/applications/SimpleButton_App.java
+++ b/src/main/java/com/pi4j/catalog/applications/SimpleButton_App.java
@@ -1,38 +1,44 @@
package com.pi4j.catalog.applications;
+import java.time.Duration;
+
import com.pi4j.context.Context;
+
import com.pi4j.catalog.Application;
-import com.pi4j.catalog.components.helpers.PIN;
+import com.pi4j.catalog.components.base.PIN;
import com.pi4j.catalog.components.SimpleButton;
/**
- * This example shows how to use the simpleButton component by registering events for different interactions with the button
+ * This example shows how to use the SimpleButton component by registering events for different interactions with the button
+ *
+ * see Description on Pi4J website
*/
public class SimpleButton_App implements Application {
+
@Override
public void execute(Context pi4j) {
- System.out.println("Simple button app started ...");
+ System.out.println("Simple button demo started ...");
// Initialize the button component
final var button = new SimpleButton(pi4j, PIN.D26, Boolean.FALSE);
// Register event handlers to print a message when pressed (onDown) and depressed (onUp)
- button.onDown (() -> System.out.println("Pressing the button"));
- button.whilePressed(() -> System.out.println("Pressing"), 1000);
- button.onUp (() -> System.out.println("Stopped pressing."));
+ button.onDown (() -> System.out.println("Button pressed"));
+ button.whilePressed(() -> System.out.println("Still pressing"), Duration.ofSeconds(1));
+ button.onUp (() -> System.out.println("Stopped pressing"));
// Wait for 15 seconds while handling events before exiting
System.out.println("Press the button to see it in action!");
- delay(15_000);
+ delay(Duration.ofSeconds(15));
// Unregister all event handlers to exit this application in a clean way
- button.deRegisterAll();
+ button.reset();
/*
if you want to deRegister only a single function, you can do so like this:
button.onUp(null);
*/
- System.out.println("Simple button app done.");
+ System.out.println("Simple button demo finished.");
}
}
\ No newline at end of file
diff --git a/src/main/java/com/pi4j/catalog/applications/SimpleLed_App.java b/src/main/java/com/pi4j/catalog/applications/SimpleLed_App.java
index d0a8e7e..cb09630 100644
--- a/src/main/java/com/pi4j/catalog/applications/SimpleLed_App.java
+++ b/src/main/java/com/pi4j/catalog/applications/SimpleLed_App.java
@@ -1,18 +1,23 @@
package com.pi4j.catalog.applications;
+import java.time.Duration;
+
import com.pi4j.context.Context;
+
import com.pi4j.catalog.Application;
-import com.pi4j.catalog.components.helpers.PIN;
+import com.pi4j.catalog.components.base.PIN;
import com.pi4j.catalog.components.SimpleLed;
/**
* This example shows how to use the simple LED component by creating a flashing light by repeatedly toggling the LED on and off.
+ *
+ * see Description on Pi4J website
*/
public class SimpleLed_App implements Application {
@Override
public void execute(Context pi4j) {
- System.out.println("Simple LED app started ...");
+ System.out.println("Simple LED demo started ...");
// Create a new SimpleLED component
SimpleLed led = new SimpleLed(pi4j, PIN.D26);
@@ -20,20 +25,18 @@ public void execute(Context pi4j) {
// Turn on the LED to have a defined state
System.out.println("Turn on LED.");
led.on();
- delay(1000);
+ delay(Duration.ofSeconds(1));
// Make a flashing light by toggling the LED every second
for (int i = 0; i < 10; i++) {
- System.out.println("Current LED state is " + led.toggleState() +".");
- delay(1000);
+ System.out.println("Current LED state is :" + led.toggle() +".");
+ delay(Duration.ofSeconds(1));
}
- // That's all so turn off the relay and quit
- led.off();
- System.out.println("Turn off LED.");
- delay(2000);
+ // That's it so reset all and quit
+ led.reset();
- System.out.println("Simple LED app done.");
+ System.out.println("Simple LED demo finished.");
}
}
diff --git a/src/main/java/com/pi4j/catalog/components/Ads1115.java b/src/main/java/com/pi4j/catalog/components/Ads1115.java
index b61fbdc..a288ea1 100644
--- a/src/main/java/com/pi4j/catalog/components/Ads1115.java
+++ b/src/main/java/com/pi4j/catalog/components/Ads1115.java
@@ -1,103 +1,16 @@
package com.pi4j.catalog.components;
-import com.pi4j.config.exception.ConfigException;
-import com.pi4j.context.Context;
-import com.pi4j.catalog.components.helpers.ContinuousMeasuringException;
-import com.pi4j.io.i2c.I2C;
-import com.pi4j.io.i2c.I2CConfig;
-
-import java.util.Arrays;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
import java.util.function.Consumer;
-public class Ads1115 extends Component {
- /**
- * PI4J i2c component
- */
- private final I2C i2c;
-
- /**
- * device name
- */
- private final static String DEVICE_ID = "ADS1115";
-
- /**
- * pi4j context
- */
- private final Context context;
-
- /**
- * bus number
- */
- private final int i2cBus;
-
- /**
- * default bus number
- */
- private final static int DEFAULT_I2C_BUS = 0x01;
-
- /**
- * default number of channels
- */
- private final static int DEFAULT_NUMBER_OF_CHANNELS = 1;
- /**
- * device address
- */
- private final ADDRESS address;
-
- /**
- * Operational status or single-shot conversion start
- */
- private final int os;
-
- /**
- * programmable gain amplifier
- */
- private final Ads1115.GAIN pga;
-
- /**
- * sampling rate of device
- */
-
- private final DR dr;
-
- /**
- * Comparator mode
- */
- private final int compMode;
-
- /**
- * Comparator polarity
- */
- private final int compPol;
-
- /**
- * Latching comparator
- */
- private final int compLat;
-
- /**
- * Comparator queue and disable
- */
- private final int compQue;
+import com.pi4j.context.Context;
+import com.pi4j.io.i2c.I2C;
- /**
- * old value from last successful read of conversion register (raw data)
- */
- private int[] oldValue = new int[4];
-
- /**
- * number of channels controlled by ad converter
- */
- private final int numberOfChannels;
- /**
- * continuous reading active
- */
- protected boolean continuousReadingActive;
- /**
- * flag which channel is continuous reading active
- */
- private boolean[] continuousReadingActiveChannel;
+import com.pi4j.catalog.components.base.I2CDevice;
+public class Ads1115 extends I2CDevice {
/**
* The Conversion register contains the result of the last conversion.
*/
@@ -115,575 +28,247 @@ public class Ads1115 extends Component {
*/
private static final int HI_THRESH_REGISTER = 0x03;
+ private final Context pi4j;
/**
- * Runnable code when current value from fast read is changed
+ * programmable gain amplifier
*/
- private Consumer
+ * The maximum allowed sampling frequency of the signal is 1/2 the sampling rate of the ADC.
+ * The reciprocal of this sampling rate finally results in the minimum response time to a signal request.
+ * (the delay of the bus is not included).
+ *
+ * This leads to the following table for the maximum allowed readFrequency by a sampling rate of 128 sps:
+ * 1 channels in use -> readFrequency max 64Hz (min. response time = 16ms)
+ * 2 channels in use -> readFrequency max 32Hz (min. response time = 32ms)
+ * 3 channels in use -> readFrequency max 21Hz (min. response time = 48ms)
+ * 4 channels in use -> readFrequency max 16Hz (min. response time = 63ms)
*
- * @return device name
+ * @param threshold threshold for triggering value change event (+- voltage)
*/
- public String getDeviceId() {
- return DEVICE_ID;
- }
+ public void startContinuousReading(double threshold) {
+ if (continuousReadingActive) {
+ throw new IllegalStateException("continuous reading already active");
+ } else {
+ //set fast continuous reading active to lock slow continuous reading
+ continuousReadingActive = true;
- /**
- * Return GAIN object with bit structure for configuration and resolution (gain per bit)
- *
- * @return GAIN object
- */
- public GAIN getPga() {
- return pga;
- }
+ readAllChannels(threshold);
- /**
- * Return sampling rate from device
- *
- * @return sampling rate
- */
- public int getSamplingRate() {
- return dr.getSpS();
+ logDebug("Start continuous reading");
+ }
}
/**
- * Sets or disables the handler for the onValueChange event.
- * This event gets triggered whenever the analog value
- * from the device changes.
- * Only a single event handler can be registered at once.
- *
- * @param method Event handler to call or null to disable
+ * stops continuous reading
*/
- public void setConsumerFastRead(Consumer
+ * Maybe works on other camera-modules too, but is not yet tested.
+ *
* It uses the libcamera-still and libcamera-vid bash commands. those are pre-installed
- * on all raspbian-versions after Buster. (Crowpi is raspbian Version Bullseye)
+ * on all raspbian-versions after Buster.
*/
-public class Camera extends Component{
+public class Camera extends Component {
+ public static PicConfig.Builder newPictureConfigBuilder(){
+ return new PicConfig.Builder();
+ }
+
+ public static VidConfig.Builder newVidConfigBuilder(){
+ return new VidConfig.Builder();
+ }
/**
* Constructor for using the picture and video functionality
@@ -25,11 +35,13 @@ public Camera() {
/**
* Takes a picture and saves it to the default Pictures folder
- *
+ *
* If a file already exists, the code will break. better use useDate while taking pictures
*/
- public void takeStill() {
- takeStill(PicConfig.Builder.newInstance().outputPath("/home/pi/Pictures/picam.jpg").build());
+ public void recordPicture() {
+ recordPicture(newPictureConfigBuilder()
+ .outputPath("/home/pi/Pictures/picam.jpg")
+ .build());
}
/**
@@ -37,7 +49,7 @@ public void takeStill() {
*
* @param config Use the ConfigBuilder of the CameraConfig to create the desired parameters
*/
- public void takeStill(PicConfig config) {
+ public void recordPicture(PicConfig config) {
logDebug("Taking Picture");
ProcessBuilder processBuilder = new ProcessBuilder();
@@ -46,17 +58,20 @@ public void takeStill(PicConfig config) {
try {
callBash(processBuilder);
} catch (Exception e) {
- logError("Camera: Error while taking picture: " + e.getMessage());
+ logException("Camera: Error while taking picture: ", e);
}
}
/**
* Takes a video and saves it to the default Videos folder
- *
+ *
* If a file already exists, the code will break. better use useDate while taking videos
*/
- public void takeVid() {
- takeVid(VidConfig.Builder.newInstance().outputPath("/home/pi/Videos/video.h264").recordTime(5000).build());
+ public void recordVideo() {
+ recordVideo(newVidConfigBuilder()
+ .outputPath("/home/pi/Videos/video.h264")
+ .recordTime(5000)
+ .build());
}
/**
@@ -64,7 +79,7 @@ public void takeVid() {
*
* @param config path to the .h264 file
*/
- public void takeVid(VidConfig config) {
+ public void recordVideo(VidConfig config) {
logDebug("Taking Video");
ProcessBuilder processBuilder = new ProcessBuilder();
@@ -73,7 +88,7 @@ public void takeVid(VidConfig config) {
try {
callBash(processBuilder);
} catch (Exception e) {
- logError("Camera: Error while taking video: " + e.getMessage());
+ logException("Camera: Error while taking video: ", e);
}
}
@@ -86,8 +101,7 @@ public void takeVid(VidConfig config) {
private void callBash(ProcessBuilder processBuilder) throws IOException, InterruptedException {
Process process = processBuilder.start();
- BufferedReader reader =
- new BufferedReader(new InputStreamReader(process.getInputStream()));
+ BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
@@ -96,10 +110,10 @@ private void callBash(ProcessBuilder processBuilder) throws IOException, Interru
//exitCode 0 = No Errors
int exitCode = process.waitFor();
- if(exitCode != 0){
- System.out.println("\nCamera exited with error code : " + exitCode);
- }else{
- System.out.println("\nCamera finished successfully");
+ if (exitCode != 0) {
+ logError("Camera exited with error code : %s", exitCode);
+ } else {
+ logInfo("Camera finished successfully");
}
}
@@ -107,7 +121,7 @@ private void callBash(ProcessBuilder processBuilder) throws IOException, Interru
* testing, if camera is installed on raspberrypi, and if the bash commands
* will work
*/
- private void init(){
+ private void init() {
logDebug("initialisation of camera");
ProcessBuilder processBuilder = new ProcessBuilder();
@@ -116,7 +130,7 @@ private void init(){
try {
callBash(processBuilder);
} catch (Exception e) {
- logError("Camera: Error at initialisation: " + e.getMessage());
+ logException("Camera: Error at initialisation: ", e);
}
}
@@ -178,26 +192,43 @@ public String getEncoding() {
* Builder Pattern to create a config for a single Picture
*/
public static class PicConfig {
- /** where should it be saved and what's the filename?*/
+ /**
+ * where should it be saved and what's the filename?
+ */
public final String outputPath;
- /** using datetime as filename?
+ /**
+ * using datetime as filename?
* if yes, then the outputPath should be a path, not a file
*/
public final boolean useDate;
- /** a delay, before taking a picture */
+ /**
+ * a delay, before taking a picture
+ */
public final int delay;
- /** output width of the picture */
+ /**
+ * output width of the picture
+ */
public final int width;
- /** output height of the picture */
+ /**
+ * output height of the picture
+ */
public final int height;
- /** the quality of the picture, ranging from 0 to 100
- * where 100 is the best quality of the picture, with no blurring*/
+ /**
+ * the quality of the picture, ranging from 0 to 100
+ * where 100 is the best quality of the picture, with no blurring
+ */
public final int quality;
- /** The format of the output */
+ /**
+ * The format of the output
+ */
public final PicEncoding encoding;
- /** when true, there is no preview on the raspberry-pi */
+ /**
+ * when true, there is no preview on the raspberry-pi
+ */
public final boolean disablePreview;
- /** when true, the preview is in fullscreen */
+ /**
+ * when true, the preview is in fullscreen
+ */
public final boolean allowFullscreenPreview;
/**
@@ -205,7 +236,7 @@ public static class PicConfig {
*
* @param builder builder with the defined options
*/
- private PicConfig(Builder builder){
+ private PicConfig(Builder builder) {
this.outputPath = builder.outputPath;
this.useDate = builder.useDate;
this.delay = builder.delay;
@@ -222,45 +253,56 @@ private PicConfig(Builder builder){
*
* @return a string that can be called from the bash
*/
- public String asCommand(){
+ public String asCommand() {
StringBuilder command = new StringBuilder("libcamera-still");
- if (useDate){
+ if (useDate) {
command.append(" -o '").append(outputPath).append(LocalDateTime.now()).append(".").append((encoding != null) ? encoding : "jpg").append("'");
- }else{
- command.append(" -o '").append(outputPath).append("'");}
- if (delay != 0){
- command.append(" -t ").append(delay);}
- if (width != 0){
- command.append(" --width ").append(width);}
- if (height != 0){
- command.append(" --height ").append(height);}
- if (quality != 0){
- command.append(" -q ").append(quality);}
- if (encoding != null){
- command.append(" --encoding ").append(encoding.getEncoding());}
- if (disablePreview){command.append(" -n");}
- if (allowFullscreenPreview && !disablePreview){command.append(" -f");}
+ } else {
+ command.append(" -o '").append(outputPath).append("'");
+ }
+ if (delay != 0) {
+ command.append(" -t ").append(delay);
+ }
+ if (width != 0) {
+ command.append(" --width ").append(width);
+ }
+ if (height != 0) {
+ command.append(" --height ").append(height);
+ }
+ if (quality != 0) {
+ command.append(" -q ").append(quality);
+ }
+ if (encoding != null) {
+ command.append(" --encoding ").append(encoding.getEncoding());
+ }
+ if (disablePreview) {
+ command.append(" -n");
+ }
+ if (allowFullscreenPreview && !disablePreview) {
+ command.append(" -f");
+ }
+
return command.toString();
}
/**
* Builder Pattern, to create a config for a single picture
- *
+ *
* A Config is buildable like this:
* var config = Camera.PicConfig.Builder.newInstance()
- * .outputPath("/home/pi/Pictures/")
- * .delay(3000)
- * .disablePreview(true)
- * .encoding(Camera.PicEncoding.PNG)
- * .useDate(true)
- * .quality(93)
- * .width(1280)
- * .height(800)
- * .build();
- *
+ * .outputPath("/home/pi/Pictures/")
+ * .delay(3000)
+ * .disablePreview(true)
+ * .encoding(Camera.PicEncoding.PNG)
+ * .useDate(true)
+ * .quality(93)
+ * .width(1280)
+ * .height(800)
+ * .build();
+ *
* Every property can be added or not.
*/
- public static class Builder{
+ public static class Builder {
private String outputPath;
private boolean useDate;
private int delay;
@@ -271,54 +313,50 @@ public static class Builder{
private boolean disablePreview;
private boolean allowFullscreenPreview;
- public static Builder newInstance(){
- return new Builder();
- }
-
- public Builder outputPath(String outputPath){
+ public Builder outputPath(String outputPath) {
this.outputPath = outputPath;
return this;
}
- public Builder useDate(boolean useDate){
+ public Builder useDate(boolean useDate) {
this.useDate = useDate;
return this;
}
- public Builder delay(int delay){
+ public Builder delay(int delay) {
this.delay = delay;
return this;
}
- public Builder width(int width){
+ public Builder width(int width) {
this.width = width;
return this;
}
- public Builder height(int height){
+ public Builder height(int height) {
this.height = height;
return this;
}
- public Builder quality(int quality){
- if(quality < 0 || quality > 100){
+ public Builder quality(int quality) {
+ if (quality < 0 || quality > 100) {
throw new IllegalArgumentException("quality must be between 0 and 100");
}
this.quality = quality;
return this;
}
- public Builder encoding(PicEncoding encoding){
+ public Builder encoding(PicEncoding encoding) {
this.encoding = encoding;
return this;
}
- public Builder disablePreview(boolean disablePreview){
+ public Builder disablePreview(boolean disablePreview) {
this.disablePreview = disablePreview;
return this;
}
- public Builder allowFullscreenPreview(boolean allowFullscreenPreview){
+ public Builder allowFullscreenPreview(boolean allowFullscreenPreview) {
this.allowFullscreenPreview = allowFullscreenPreview;
return this;
}
@@ -333,15 +371,22 @@ public PicConfig build() {
* Builder Pattern to create a config for a video
*/
public static class VidConfig {
- /** where should it be saved and what's the filename?*/
+ /**
+ * where should it be saved and what's the filename?
+ */
public final String outputPath;
- /** using datetime as filename?
+ /**
+ * using datetime as filename?
* if yes, then the outputPath should be a path, not a file
*/
public final boolean useDate;
- /** the length in milliseconds, how long the camera is actively filming */
+ /**
+ * the length in milliseconds, how long the camera is actively filming
+ */
public final int recordTime;
- /** the output-format of the video-file */
+ /**
+ * the output-format of the video-file
+ */
public final VidEncoding encoding;
/**
@@ -349,7 +394,7 @@ public static class VidConfig {
*
* @param builder builder with the defined options
*/
- private VidConfig(Builder builder){
+ private VidConfig(Builder builder) {
this.outputPath = builder.outputPath;
this.recordTime = builder.recordTime;
this.encoding = builder.encoding;
@@ -361,55 +406,53 @@ private VidConfig(Builder builder){
*
* @return a string that can be called from the bash
*/
- public String asCommand(){
+ public String asCommand() {
StringBuilder command = new StringBuilder("libcamera-vid -t " + recordTime);
- if (useDate){
+ if (useDate) {
command.append(" -o '").append(outputPath).append(LocalDateTime.now()).append(".").append((encoding != null) ? encoding : "h264").append("'");
- }else{
- command.append(" -o '").append(outputPath).append("'");}
- if(encoding != null){
- command.append(" --codec ").append(encoding.getEncoding());}
+ } else {
+ command.append(" -o '").append(outputPath).append("'");
+ }
+ if (encoding != null) {
+ command.append(" --codec ").append(encoding.getEncoding());
+ }
return command.toString();
}
/**
* Builder Pattern, to create a config for a video
- *
+ *
* A Config is buildable like this:
* var vidconfig = Camera.VidConfig.Builder.newInstance()
- * .outputPath("/home/pi/Videos/")
- * .recordTime(3000)
- * .useDate(true)
- * .build();
- *
+ * .outputPath("/home/pi/Videos/")
+ * .recordTime(3000)
+ * .useDate(true)
+ * .build();
+ *
* Every Property can be added or not.
*/
- public static class Builder{
+ public static class Builder {
private String outputPath;
private int recordTime;
private VidEncoding encoding;
private boolean useDate;
- public static Builder newInstance(){
- return new Builder();
- }
-
- public Builder outputPath(String outputPath){
+ public Builder outputPath(String outputPath) {
this.outputPath = outputPath;
return this;
}
- public Builder recordTime(int recordTime){
+ public Builder recordTime(int recordTime) {
this.recordTime = recordTime;
return this;
}
- public Builder encoding(VidEncoding encoding){
+ public Builder encoding(VidEncoding encoding) {
this.encoding = encoding;
return this;
}
- public Builder useDate(boolean useDate){
+ public Builder useDate(boolean useDate) {
this.useDate = useDate;
return this;
}
diff --git a/src/main/java/com/pi4j/catalog/components/Component.java b/src/main/java/com/pi4j/catalog/components/Component.java
deleted file mode 100644
index 1d24440..0000000
--- a/src/main/java/com/pi4j/catalog/components/Component.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.pi4j.catalog.components;
-
-import java.util.logging.Logger;
-
-public abstract class Component {
- /**
- * Logger instance
- */
- private final Logger logger = Logger.getLogger(getClass().getName());
-
- protected void logInfo(String msg) {
- logger.info(() -> msg);
- }
-
- protected void logError(String msg) {
- logger.severe(() -> msg);
- }
-
- protected void logConfig(String msg) {
- logger.config(() -> msg);
- }
-
- protected void logDebug(String msg) {
- logger.fine(() -> msg);
- }
-
- /**
- * Utility function to sleep for the specified amount of milliseconds.
- * An {@link InterruptedException} will be caught and ignored while setting the interrupt flag again.
- *
- * @param milliseconds Time in milliseconds to sleep
- */
- void delay(long milliseconds) {
- try {
- Thread.sleep(milliseconds);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
-}
diff --git a/src/main/java/com/pi4j/catalog/components/Joystick.java b/src/main/java/com/pi4j/catalog/components/Joystick.java
index 7868cf4..4ea32d5 100644
--- a/src/main/java/com/pi4j/catalog/components/Joystick.java
+++ b/src/main/java/com/pi4j/catalog/components/Joystick.java
@@ -1,22 +1,17 @@
package com.pi4j.catalog.components;
+import java.time.Duration;
+
import com.pi4j.context.Context;
-import com.pi4j.catalog.components.helpers.PIN;
-import com.pi4j.io.gpio.digital.DigitalInput;
-import com.pi4j.io.gpio.digital.DigitalState;
+import com.pi4j.plugin.mock.provider.gpio.digital.MockDigitalInput;
-import java.util.ArrayList;
-import java.util.List;
+import com.pi4j.catalog.components.base.Component;
+import com.pi4j.catalog.components.base.PIN;
/**
* Implementation of a joystick using 5 GPIO up, left, down, right and push with Pi4J
*/
-public class Joystick extends Component{
-
- /**
- * Default debounce time in microseconds
- */
- protected static final long DEFAULT_DEBOUNCE = 10000;
+public class Joystick extends Component {
/**
* Button component for joystick direction up
@@ -38,10 +33,6 @@ public class Joystick extends Component{
* Button component for joystick push
*/
private final SimpleButton bPush;
- /**
- * Specifies if Joystick with push button
- */
- private final boolean pushIsPresent;
/**
* Creates a new joystick component with 5 custom GPIO address, a joystick with push button.
@@ -54,13 +45,18 @@ public class Joystick extends Component{
* @param addrPush GPIO address of button push
*/
public Joystick (Context pi4j, PIN addrNorth, PIN addrEast, PIN addrSouth, PIN addrWest, PIN addrPush){
- bNorth = new SimpleButton(pi4j, addrNorth, false, DEFAULT_DEBOUNCE);
- bWest = new SimpleButton(pi4j, addrWest, false, DEFAULT_DEBOUNCE);
- bSouth = new SimpleButton(pi4j, addrSouth, false, DEFAULT_DEBOUNCE);
- bEast = new SimpleButton(pi4j, addrEast, false, DEFAULT_DEBOUNCE);
- //joystick with push button
- bPush = new SimpleButton(pi4j, addrPush, false, DEFAULT_DEBOUNCE);
- pushIsPresent = true;
+ bNorth = new SimpleButton(pi4j, addrNorth, false);
+ bWest = new SimpleButton(pi4j, addrWest, false);
+ bSouth = new SimpleButton(pi4j, addrSouth, false);
+ bEast = new SimpleButton(pi4j, addrEast, false);
+
+ //joystick has push button
+ if(addrPush != null){
+ bPush = new SimpleButton(pi4j, addrPush, false);
+ }
+ else {
+ bPush = null;
+ }
}
/**
@@ -73,147 +69,65 @@ public Joystick (Context pi4j, PIN addrNorth, PIN addrEast, PIN addrSouth, PIN a
* @param addrEast GPIO address of button right
*/
public Joystick (Context pi4j, PIN addrNorth, PIN addrEast, PIN addrSouth, PIN addrWest){
- bNorth = new SimpleButton(pi4j, addrNorth, false, DEFAULT_DEBOUNCE);
- bWest = new SimpleButton(pi4j, addrWest, false, DEFAULT_DEBOUNCE);
- bSouth = new SimpleButton(pi4j, addrSouth, false, DEFAULT_DEBOUNCE);
- bEast = new SimpleButton(pi4j, addrEast, false, DEFAULT_DEBOUNCE);
- bPush = null;
- //joystick without push button
- pushIsPresent = false;
- }
-
- /**
- * Returns a list of current state of the touch sensors
- *
- * @return a list of button states
- */
- public List
+ * In this implementation we use an 'Ads115' and attach two 'Potentiometer', using 2 of the ADC channels,
+ * and one 'SimpleButton', connected to one of the digital pins, to it.
+ *
+ * We use the terms 'normalized value' and 'raw value'.
+ *
+ * Works with the PCF8574T backpack, only.
*/
-public class LcdDisplay extends Component {
+public class LcdDisplay extends I2CDevice {
/** Flags for display commands */
private static final byte LCD_CLEAR_DISPLAY = (byte) 0x01;
private static final byte LCD_RETURN_HOME = (byte) 0x02;
+ private static final byte LCD_SCROLL_RIGHT = (byte) 0x1E;
+ private static final byte LCD_SCROLL_LEFT = (byte) 0x18;
private static final byte LCD_ENTRY_MODE_SET = (byte) 0x04;
private static final byte LCD_DISPLAY_CONTROL = (byte) 0x08;
private static final byte LCD_CURSOR_SHIFT = (byte) 0x10;
@@ -33,8 +39,6 @@ public class LcdDisplay extends Component {
// flags for display/cursor shift
private static final byte LCD_DISPLAY_MOVE = (byte) 0x08;
private static final byte LCD_CURSOR_MOVE = (byte) 0x00;
- private static final byte LCD_MOVE_RIGHT = (byte) 0x04;
- private static final byte LCD_MOVE_LEFT = (byte) 0x00;
// flags for function set
private static final byte LCD_8BIT_MODE = (byte) 0x10;
private static final byte LCD_4BIT_MODE = (byte) 0x00;
@@ -53,32 +57,19 @@ public class LcdDisplay extends Component {
* Display row offsets. Offset for up to 4 rows.
*/
private static final byte[] LCD_ROW_OFFSETS = {0x00, 0x40, 0x14, 0x54};
- /**
- * The Default BUS and Device Address.
- * On the PI, you can look it up with the Command 'sudo i2cdetect -y 1'
- */
- private static final int DEFAULT_BUS = 0x1;
+
private static final int DEFAULT_DEVICE = 0x27;
/**
- * The PI4J I2C component
- */
- private final I2C i2c;
- /**
- * The amount of rows on the display
+ * Number of rows on the display
*/
private final int rows;
/**
- * The amount of columns on the display
+ * Number of columns on the display
*/
private final int columns;
-
- /**
- * With this Byte cursor visibility is controlled
- */
- private byte displayControl;
/**
- * This boolean checks if the backlight is on or not
+ * Is backlight is on or off
*/
private boolean backlight;
@@ -88,201 +79,195 @@ public class LcdDisplay extends Component {
* @param pi4j Pi4J context
*/
public LcdDisplay(Context pi4j){
- this(pi4j, 2, 16);
+ this(pi4j, 2, 16, DEFAULT_DEVICE);
}
/**
* Creates a new LCDDisplay component with custom rows and columns
*
* @param pi4j Pi4J context
- * @param rows Custom amount of display lines
- * @param columns Custom amount of chars on line
+ * @param rows amount of display lines
+ * @param columns amount of chars on each line
*/
- public LcdDisplay(Context pi4j, int rows, int columns) {
- this.rows = rows;
- this.columns = columns;
- this.i2c = pi4j.create(buildI2CConfig(pi4j, DEFAULT_BUS, DEFAULT_DEVICE));
- init();
+ public LcdDisplay(Context pi4j, int rows, int columns){
+ this(pi4j, rows, columns, DEFAULT_DEVICE);
}
/**
* Creates a new LCDDisplay component with custom rows and columns
*
* @param pi4j Pi4J context
- * @param rows Custom amount of display lines
- * @param columns Custom amount of chars on line
- * @param bus Custom I2C bus address
- * @param device Custom I2C device Address
+ * @param rows amount of display lines
+ * @param columns amount of chars on each line
+ * @param device I2C device address
*/
- public LcdDisplay(Context pi4j, int rows, int columns, int bus, int device) {
- this.rows = rows;
+ public LcdDisplay(Context pi4j, int rows, int columns, int device) {
+ super(pi4j, device, "PCF8574AT backed LCD");
+ this.rows = rows;
this.columns = columns;
- this.i2c = pi4j.create(buildI2CConfig(pi4j, bus, device));
- init();
}
+
/**
- * Registers the I2C Component on the PI4J
- *
- * @param pi4j The Context
- * @param bus The I2C Bus
- * @param device The I2C Device
- * @return A Provider of an I2C Bus
+ * Initializes the LCD with the backlight off
*/
- private I2CConfig buildI2CConfig(Context pi4j, int bus, int device) {
- return I2C.newConfigBuilder(pi4j)
- .id("I2C-" + device + "@" + bus)
- .name("PCF8574AT")
- .bus(bus)
- .device(device)
- .build();
- }
+ @Override
+ protected void init(I2C i2c) {
+ sendLcdTwoPartsCommand((byte) 0x03);
+ sendLcdTwoPartsCommand((byte) 0x03);
+ sendLcdTwoPartsCommand((byte) 0x03);
+ sendLcdTwoPartsCommand((byte) 0x02);
+ // Initialize display settings
+ sendLcdTwoPartsCommand((byte) (LCD_FUNCTION_SET | LCD_2LINE | LCD_5x8DOTS | LCD_4BIT_MODE));
+ sendLcdTwoPartsCommand((byte) (LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON | LCD_CURSOR_OFF | LCD_BLINK_OFF));
+ sendLcdTwoPartsCommand((byte) (LCD_ENTRY_MODE_SET | LCD_ENTRY_LEFT | LCD_ENTRY_SHIFT_DECREMENT));
- /**
- * @return The current running Context
- */
- public I2C getI2C(){
- return this.i2c;
+ clearDisplay();
+
+ // Enable backlight
+ setDisplayBacklight(true);
}
/**
* Turns the backlight on or off
*/
- public void setDisplayBacklight(boolean state) {
- this.backlight = state;
- executeCommand(this.backlight ? LCD_BACKLIGHT : LCD_NO_BACKLIGHT);
+ public void setDisplayBacklight(boolean backlightEnabled) {
+ backlight = backlightEnabled;
+ sendCommand(backlight ? LCD_BACKLIGHT : LCD_NO_BACKLIGHT);
}
/**
* Clear the LCD and set cursor to home
*/
public void clearDisplay() {
- writeCommand(LCD_CLEAR_DISPLAY);
moveCursorHome();
+ sendLcdTwoPartsCommand(LCD_CLEAR_DISPLAY);
}
/**
* Returns the Cursor to Home Position (First line, first character)
*/
public void moveCursorHome() {
- writeCommand(LCD_RETURN_HOME);
- sleep(3, 0);
+ sendLcdTwoPartsCommand(LCD_RETURN_HOME);
}
/**
* Shuts the display off
*/
public void off() {
- executeCommand(LCD_DISPLAY_OFF);
+ sendCommand(LCD_DISPLAY_OFF);
}
/**
- * Write a Line of Text on the LCD Display
+ * Write a line of text on the LCD
*
- * @param text Text to display
- * @param line Select Line of Display
+ * @param text Text to be displayed
+ * @param line linenumber of display, range: 0 .. rows-1
*/
- public void displayText(String text, int line) {
- if (text.length() > columns) {
- throw new IllegalArgumentException("Too long text. Only " + columns + " characters possible");
- }
- if (line > rows || line < 1) {
- throw new IllegalArgumentException("Wrong line id. Only " + rows + " lines possible");
- }
+ public void displayLineOfText(String text, int line) {
+ displayLineOfText(text, line, 0);
+ }
- clearLine(line);
- moveCursorHome();
- displayLine(text, LCD_ROW_OFFSETS[line - 1]);
+ /**
+ * Center specified text in specified line
+ * @param text Text to be displayed
+ * @param line linenumber of display, range: 0 .. rows-1
+ */
+ public void centerTextInLine(String text, int line){
+ displayLineOfText(text, line, (int) ((columns - text.length()) * 0.5));
}
/**
- * Write a Line of Text on the LCD Display
+ * Write a line of text on the LCD
*
- * @param text Text to display
- * @param line Select Line of Display
- * @param position Select position on line
- */
- public void displayText(String text, int line, int position) {
- if (position > columns) {
- throw new IllegalArgumentException("Too long text. Only " + columns + " characters possible");
- }
- if (line > rows || line < 1) {
- throw new IllegalArgumentException("Wrong line id. Only " + rows + " lines possible");
+ * @param text text to be displayed
+ * @param line line number of display, range: 0..rows-1
+ * @param position start position, range: 0..columns-1
+ */
+ public void displayLineOfText(String text, int line, int position) {
+ if (text.length() + position > columns) {
+ logInfo("Text '%s' too long, cut to %d characters", text, (columns - position));
+ text = text.substring(0, (columns - position));
}
- clearLine(line);
- setCursorToPosition(position, line);
- for (char character: text.toCharArray()) {
- writeCharacter(character);
+ if (line > rows || line < 0) {
+ logError("Wrong line id '%d'. Only %d lines possible", line, rows);
+ }
+ else {
+ setCursorToPosition(line, 0);
+ for(int i= 0; i < position; i++){
+ writeCharacter(' ');
+ }
+ for (char character : text.toCharArray()) {
+ writeCharacter(character);
+ }
+ for(int i = 0; i< columns - text.length(); i++){
+ writeCharacter(' ');
+ }
}
}
/**
- * Write Text on the LCD Display
+ * Write text on the LCD starting in home position
*
* @param text Text to display
*/
public void displayText(String text) {
- if (text.length() > (rows * columns)) {
- throw new IllegalArgumentException("Too long text. Only " + rows * columns + " characters allowed");
- }
-
- // Clean and prepare to write some text
+ logDebug("Display in LCD: '%s'", text);
var currentLine = 0;
- String[] lines = new String[rows];
+
+ StringBuilder[] texts = new StringBuilder[rows];
for (int j = 0; j < rows; j++) {
- lines[j] = "";
+ texts[j] = new StringBuilder(rows);
}
- clearDisplay();
- // Iterate through lines and characters and write them to the display
for (int i = 0; i < text.length(); i++) {
- // line break in text found
- if (text.charAt(i) == '\n' && currentLine < rows) {
+ if (currentLine > rows - 1) {
+ logInfo("Text too long, remaining '%s' will not be displayed", text.substring(i));
+ break;
+ }
+ if (text.charAt(i) == '\n') {
currentLine++;
- //if a space comes after newLine, it is omitted
- if (i+1 >= text.length()) return;
- if (text.charAt(i + 1) == ' ') i++;
continue;
}
-
- // Write character to array
- lines[currentLine] += (char) text.charAt(i);
-
- if (lines[currentLine].length() == columns && currentLine < rows) {
+ else if(texts[currentLine].length() >= columns){
currentLine++;
- //if a space comes after newLine, it is omitted
- if (i+1 >= text.length()) return;
- if (text.charAt(i + 1) == ' ') i++;
+ if (text.charAt(i) == ' ') {
+ i++;
+ }
+ }
+ // append character to line
+ if(currentLine < rows){
+ texts[currentLine].append(text.charAt(i));
}
}
- //display the created Rows
+ //display the created texts
for (int j = 0; j < rows; j++) {
- displayLine(lines[j], LCD_ROW_OFFSETS[j]);
+ displayLineOfText(texts[j].toString(), j);
}
}
/**
- * write a character to lcd
+ * write a character to LCD at current cursor position
*
- * @param charvalue of the char that is written
+ * @param character char that is written
*/
- public void writeCharacter(char charvalue) {
- writeSplitCommand((byte) charvalue, Rs);
+ public void writeCharacter(char character) {
+ sendLcdTwoPartsCommand((byte) character, Rs);
}
/**
- * write a character to lcd
+ * write a character to lcd at a specific position
*
- * @param charvalue of the char that is written
- * @param line ROW-position, Range 1 - ROWS
- * @param column Column-position, Range 0 - COLUMNS-1
+ * @param character char that is written
+ * @param line row-position, Range 0 .. rows-1
+ * @param pos col-position, Range 0 .. columns-1
*/
- public void writeCharacter(char charvalue, int column, int line) {
- setCursorToPosition(column, line);
- writeSplitCommand((byte) charvalue, Rs);
+ public void writeCharacter(char character, int line, int pos) {
+ setCursorToPosition(line, pos);
+ sendLcdTwoPartsCommand((byte) character, Rs);
}
/**
@@ -292,9 +277,8 @@ public void writeCharacter(char charvalue, int column, int line) {
* @param pos for the start of the text
*/
private void displayLine(String text, int pos) {
- writeCommand((byte) (0x80 + pos));
+ sendLcdTwoPartsCommand((byte) (0x80 + pos));
- if (text == null || text.isEmpty()) return;
for (int i = 0; i < text.length(); i++) {
writeCharacter(text.charAt(i));
}
@@ -303,7 +287,7 @@ private void displayLine(String text, int pos) {
/**
* Clears a line of the display
*
- * @param line Select line to clear
+ * @param line line number of line to be cleared
*/
public void clearLine(int line) {
if (line > rows || line < 1) {
@@ -312,126 +296,21 @@ public void clearLine(int line) {
displayLine(" ".repeat(columns), LCD_ROW_OFFSETS[line - 1]);
}
- /**
- * Write a command to the LCD
- */
- private void writeCommand(byte cmd) {
- writeSplitCommand(cmd, (byte) 0);
- }
-
- /**
- * Write a command in 2 parts to the LCD
- */
- private void writeSplitCommand(byte cmd, byte mode) {
- //bitwise AND with 11110000 to remove last 4 bits
- writeFourBits((byte) (mode | (cmd & 0xF0)));
- //bitshift and bitwise AND to remove first 4 bits
- writeFourBits((byte) (mode | ((cmd << 4) & 0xF0)));
- }
-
- /**
- * Write the four bits of a byte to the LCD
- *
- * @param data the byte that is sent
- */
- private void writeFourBits(byte data) {
- i2c.write((byte) (data | (backlight ? LCD_BACKLIGHT : LCD_NO_BACKLIGHT)));
- lcd_strobe(data);
- }
-
- /**
- * Clocks EN to latch command
- */
- private void lcd_strobe(byte data) {
- i2c.write((byte) (data | En | (backlight ? LCD_BACKLIGHT : LCD_NO_BACKLIGHT)));
- sleep(0, 500_000);
- i2c.write((byte) ((data & ~En) | (backlight ? LCD_BACKLIGHT : LCD_NO_BACKLIGHT)));
- sleep(0, 100_000);
- }
-
- /**
- * Moves the cursor 1 character right
- */
- public void moveCursorRight() {
- executeCommand(LCD_CURSOR_SHIFT, (byte) (LCD_CURSOR_MOVE | LCD_MOVE_RIGHT));
- delay(1);
- }
-
- /**
- * Moves the cursor 1 character left
- */
- public void moveCursorLeft() {
- executeCommand(LCD_CURSOR_SHIFT, (byte) (LCD_CURSOR_MOVE | LCD_MOVE_LEFT));
- delay(1);
- }
-
/**
* Sets the cursor to a target destination
*
- * @param digit Selects the character of the line. Range: 0 - Columns-1
- * @param line Selects the line of the display. Range: 1 - ROWS
+ * @param line Selects the line of the display. Range: 0 - ROWS-1
+ * @param pos Selects the character of the line. Range: 0 - Columns-1
*/
- public void setCursorToPosition(int digit, int line) {
- if (line > rows || line < 1) {
+ public void setCursorToPosition(int line, int pos) {
+ if (line > rows-1 || line < 0) {
throw new IllegalArgumentException("Line out of range. Display has only " + rows + "x" + columns + " Characters!");
}
- if (digit < 0 || digit > columns) {
+ if (pos < 0 || pos > columns-1) {
throw new IllegalArgumentException("Line out of range. Display has only " + rows + "x" + columns + " Characters!");
}
- writeCommand((byte) (LCD_SET_DDRAM_ADDR | digit + LCD_ROW_OFFSETS[line - 1]));
- }
-
- /**
- * Set the cursor to desired line
- *
- * @param line Sets the cursor to this line. Only Range 1 - lines allowed.
- */
- public void setCursorToLine(int line) {
- setCursorToPosition(0, line);
- }
-
- /**
- * Sets the display cursor to hidden or showing
- *
- * @param show Set the state of the cursor
- */
- public void setCursorVisibility(boolean show) {
- if (show) {
- this.displayControl |= LCD_CURSOR_ON;
- } else {
- this.displayControl &= ~LCD_CURSOR_ON;
- }
- executeCommand(LCD_DISPLAY_CONTROL, this.displayControl);
- }
-
- /**
- * Set the cursor to blinking or static
- *
- * @param blink Blink = true means the cursor will change to blinking mode. False lets the cursor stay static
- */
- public void setCursorBlinking(boolean blink) {
- if (blink) {
- this.displayControl |= LCD_BLINK_ON;
- } else {
- this.displayControl &= ~LCD_BLINK_ON;
- }
-
- executeCommand(LCD_DISPLAY_CONTROL, this.displayControl);
- }
-
- /**
- * Moves the whole displayed text one character right
- */
- public void moveDisplayRight() {
- executeCommand(LCD_CURSOR_SHIFT, (byte) (LCD_DISPLAY_MOVE | LCD_MOVE_RIGHT));
- }
-
- /**
- * Moves the whole displayed text one character right
- */
- public void moveDisplayLeft() {
- executeCommand(LCD_CURSOR_SHIFT, (byte) (LCD_DISPLAY_MOVE | LCD_MOVE_LEFT));
+ sendLcdTwoPartsCommand((byte) (LCD_SET_DDRAM_ADDR | pos + LCD_ROW_OFFSETS[line]));
}
/**
@@ -450,62 +329,62 @@ public void createCharacter(int location, byte[] character) {
if (location > 7 || location < 1) {
throw new IllegalArgumentException("Invalid memory location. Range 1-7 allowed. Value: " + location);
}
- writeCommand((byte) (LCD_SET_CGRAM_ADDR | location << 3));
+ sendLcdTwoPartsCommand((byte) (LCD_SET_CGRAM_ADDR | location << 3));
for (int i = 0; i < 8; i++) {
- writeSplitCommand(character[i], (byte) 1);
+ sendLcdTwoPartsCommand(character[i], (byte) 1);
}
}
-
/**
- * Execute Display commands
- *
- * @param command Select the LCD Command
- * @param data Setup command data
+ * Scroll whole display to the right by one column.
*/
- private void executeCommand(byte command, byte data) {
- executeCommand((byte) (command | data));
+ public void scrollRight(){
+ sendLcdTwoPartsCommand(LCD_SCROLL_RIGHT);
}
/**
- * Write a single command
+ * Scroll whole display to the left by one column.
*/
- private void executeCommand(byte cmd) {
- i2c.write(cmd);
- sleep(0, 100_000);
+ public void scrollLeft(){
+ sendLcdTwoPartsCommand(LCD_SCROLL_LEFT);
+ }
+
+ @Override
+ public void reset() {
+ clearDisplay();
+ off();
}
/**
- * Initializes the LCD with the backlight off
+ * Write a command to the LCD
*/
- private void init() {
- writeCommand((byte) 0x03);
- writeCommand((byte) 0x03);
- writeCommand((byte) 0x03);
- writeCommand((byte) 0x02);
-
- // Initialize display settings
- writeCommand((byte) (LCD_FUNCTION_SET | LCD_2LINE | LCD_5x8DOTS | LCD_4BIT_MODE));
- writeCommand((byte) (LCD_DISPLAY_CONTROL | LCD_DISPLAY_ON | LCD_CURSOR_OFF | LCD_BLINK_OFF));
- writeCommand((byte) (LCD_ENTRY_MODE_SET | LCD_ENTRY_LEFT | LCD_ENTRY_SHIFT_DECREMENT));
-
- clearDisplay();
+ private void sendLcdTwoPartsCommand(byte cmd) {
+ sendLcdTwoPartsCommand(cmd, (byte) 0);
+ }
- // Enable backlight
- setDisplayBacklight(true);
- logDebug("LCD Display initialized");
+ /**
+ * Write a command in 2 parts to the LCD
+ */
+ private void sendLcdTwoPartsCommand(byte cmd, byte mode) {
+ //bitwise AND with 11110000 to remove last 4 bits
+ writeFourBits((byte) (mode | (cmd & 0xF0)));
+ //bitshift and bitwise AND to remove first 4 bits
+ writeFourBits((byte) (mode | ((cmd << 4) & 0xF0)));
}
/**
- * Utility function to sleep for the specified amount of milliseconds. An {@link InterruptedException} will be catched and ignored while setting the
- * interrupt flag again.
+ * Write the four bits of a byte to the LCD
+ *
+ * @param data the byte that is sent
*/
- protected void sleep(long millis, int nanos) {
- try {
- Thread.sleep(millis, nanos);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
+ private void writeFourBits(byte data) {
+ byte backlightStatus = backlight ? LCD_BACKLIGHT : LCD_NO_BACKLIGHT;
+
+ write((byte) (data | En | backlightStatus));
+ write((byte) ((data & ~En) | backlightStatus));
+
+ delay(Duration.ofNanos(50_000));
}
+
}
diff --git a/src/main/java/com/pi4j/catalog/components/LedButton.java b/src/main/java/com/pi4j/catalog/components/LedButton.java
index af70c91..e8d09fb 100644
--- a/src/main/java/com/pi4j/catalog/components/LedButton.java
+++ b/src/main/java/com/pi4j/catalog/components/LedButton.java
@@ -1,18 +1,20 @@
package com.pi4j.catalog.components;
+import java.time.Duration;
+
import com.pi4j.context.Context;
-import com.pi4j.catalog.components.helpers.PIN;
-import com.pi4j.io.gpio.digital.*;
+import com.pi4j.plugin.mock.provider.gpio.digital.MockDigitalInput;
+import com.pi4j.plugin.mock.provider.gpio.digital.MockDigitalOutput;
+
+import com.pi4j.catalog.components.base.Component;
+import com.pi4j.catalog.components.base.PIN;
+
+import static com.pi4j.io.gpio.digital.DigitalInput.DEFAULT_DEBOUNCE;
/**
- * Implementation of a button using GPIO with Pi4J
+ * Implementation of a button with integrated LED using GPIO with Pi4J.
*/
-public class LedButton extends Component {
- /**
- * Default debounce time in microseconds
- */
- protected static final long DEFAULT_DEBOUNCE = 10000;
-
+public class LedButton extends Component {
/**
* Button component
*/
@@ -44,19 +46,16 @@ public LedButton(Context pi4j, PIN buttonAddress, Boolean inverted, PIN ledAddre
* @param debounce Debounce time in microseconds
*/
public LedButton(Context pi4j, PIN buttonAddress, boolean inverted, PIN ledAddress, long debounce) {
- this.button = new SimpleButton(pi4j, buttonAddress, inverted, debounce);
- this.led = new SimpleLed(pi4j, ledAddress);
+ this(new SimpleButton(pi4j, buttonAddress, inverted, debounce),
+ new SimpleLed(pi4j, ledAddress));
}
- /**
- * Set the LED on or off depending on the boolean argument.
- *
- * @param on Sets the LED to on (true) or off (false)
- */
- public void ledSetState(boolean on) {
- led.setState(on);
+ public LedButton(SimpleButton button, SimpleLed led){
+ this.button = button;
+ this.led = led;
}
+
/**
* Sets the LED to on.
*/
@@ -76,32 +75,16 @@ public void ledOff() {
*
* @return Return true or false according to the new state of the relay.
*/
- public boolean ledToggleState() {
- return led.toggleState();
+ public boolean toggleLed() {
+ return led.toggle();
}
- /**
- * Returns the instance of the digital output
- *
- * @return DigitalOutput instance of the LED
- */
- public DigitalOutput ledGetDigitalOutput() {
- return led.getDigitalOutput();
- }
-
- /**
- * Returns the current state of the Digital State
- *
- * @return Current DigitalInput state (Can be HIGH, LOW or UNKNOWN)
- */
- public DigitalState btnGetState() { return button.getState(); }
-
/**
* Checks if button is currently pressed
*
* @return True if button is pressed
*/
- public boolean btnIsDown() {
+ public boolean isDown() {
return button.isDown();
}
@@ -110,64 +93,57 @@ public boolean btnIsDown() {
*
* @return True if button is depressed
*/
- public boolean btnIsUp() {
+ public boolean isUp() {
return button.isUp();
}
- /**
- * Returns the Pi4J DigitalInput associated with this component.
- *
- * @return Returns the Pi4J DigitalInput associated with this component.
- */
- public DigitalInput btnGetDigitalInput() {
- return button.getDigitalInput();
- }
-
/**
* Sets or disables the handler for the onDown event.
* This event gets triggered whenever the button is pressed.
* Only a single event handler can be registered at once.
*
- * @param method Event handler to call or null to disable
+ * @param task Event handler to call or null to disable
*/
- public void btnOnDown(Runnable method) { button.onDown(method); }
+ public void onDown(Runnable task) {
+ button.onDown(task);
+ }
/**
* Sets or disables the handler for the onUp event.
* This event gets triggered whenever the button is no longer pressed.
* Only a single event handler can be registered at once.
*
- * @param method Event handler to call or null to disable
+ * @param task Event handler to call or null to disable
*/
- public void btnOnUp(Runnable method) {
- button.onUp(method);
+ public void onUp(Runnable task) {
+ button.onUp(task);
}
+
/**
* Sets or disables the handler for the whilePressed event.
* This event gets triggered whenever the button is pressed.
* Only a single event handler can be registered at once.
*
- * @param method Event handler to call or null to disable
+ * @param task Event handler to call or null to disable
*/
- public void btnWhilePressed(Runnable method, long millis) {button.whilePressed(method, millis); }
+ public void whilePressed(Runnable task, Duration delay) {
+ button.whilePressed(task, delay);
+ }
- /**
- * disables all the handlers for the onUp, onDown and whilePressed Events
- */
- public void btnDeRegisterAll(){ button.deRegisterAll(); }
+ @Override
+ public void reset(){
+ button.reset();
+ led.reset();
+ }
- /**
- * @return the current Runnable that is set
- */
- public Runnable btnGetOnUp(){return button.getOnUp();}
+ // --------------- for testing --------------------
- /**
- * @return the current Runnable that is set
- */
- public Runnable btnGetOnDown(){return button.getOnDown();}
+ public MockDigitalOutput mockLed() {
+ return led.mock();
+ }
+
+ public MockDigitalInput mockButton() {
+ return button.mock();
+ }
- /**
- * @return the current Runnable that is set
- */
- public Runnable btnGetWhilePressed(){return button.getWhilePressed();}
}
\ No newline at end of file
diff --git a/src/main/java/com/pi4j/catalog/components/LedMatrix.java b/src/main/java/com/pi4j/catalog/components/LedMatrix.java
index 837af4b..a0c5ae3 100644
--- a/src/main/java/com/pi4j/catalog/components/LedMatrix.java
+++ b/src/main/java/com/pi4j/catalog/components/LedMatrix.java
@@ -1,57 +1,27 @@
package com.pi4j.catalog.components;
+import java.time.Duration;
+
import com.pi4j.context.Context;
-import com.pi4j.io.spi.Spi;
-import java.util.Arrays;
+import com.pi4j.catalog.components.base.Component;
/**
- * Creates an SPI Control for Neopixel LED Strips
+ * Creates an SPI Control for Neopixel for a LED matrix consisting of a single LED Strip.
+ *
+ * It's more or less a pure convenience class to get an API more appropriate to operate with a matrix.
+ * All calls are delegated to the LED strip
*/
public class LedMatrix extends Component {
- /**
- * Default Channel of the SPI Pins
- */
- protected static final int DEFAULT_SPI_CHANNEL = 0;
-
- /**
- * The PI4J SPI
- */
- protected final Spi spi;
- /**
- * The PI4J context
- */
- protected final Context context;
- /**
- * The matrix, that includes all pixels of all LEDStrips
- */
- private final int[][] matrix;
/**
* The corresponding LEDStrip to the matrix. The matrix is nothing more than just an array from LEDStrips, that
* can be calculated to a single LEDStrip
*/
private final LedStrip ledStrip;
- /**
- * Brightness value between 0 and 1
- */
- private double brightness;
- private int numLEDs;
+ private final int rows;
+ private final int columns;
- /**
- * Create a new LEDMatrix with the defined Matrix.
- * You can give in something like int[3][4] or
- * matrix = {{0, 0, 0},
- * {0, 0, 0, 0},
- * {0}}
- *
- * @param pi4j Pi4J context
- * @param matrix How many LEDs are on this Strand
- * @param brightness How bright the LEDs can be at max, Range 0 - 1
- */
- public LedMatrix(Context pi4j, int[][] matrix, double brightness) {
- this(pi4j, matrix, brightness, DEFAULT_SPI_CHANNEL);
- }
/**
* Creates a new LEDMatrix with the defined rows and columns
@@ -61,154 +31,58 @@ public LedMatrix(Context pi4j, int[][] matrix, double brightness) {
* @param columns How many columns of LED
* @param brightness How bright the LEDs can be at max, Range 0 - 1
*/
- public LedMatrix(Context pi4j, int rows, int columns, double brightness) {
- this(pi4j, new int[rows][columns], brightness, DEFAULT_SPI_CHANNEL);
+ public LedMatrix(Context pi4j, int rows, int columns) {
+ this.rows = rows;
+ this.columns = columns;
+ this.ledStrip = new LedStrip(pi4j, rows*columns);
}
- /**
- * Creates a new LEDMatrix component with a custom BCM pin.
- *
- * @param pi4j Pi4J context
- * @param matrix How many LEDs are on this Strand
- * @param brightness How bright the LEDs can be at max, Range 0 - 255
- * @param channel which channel to use
- */
- public LedMatrix(Context pi4j, int[][] matrix, double brightness, int channel) {
- this.matrix = matrix;
- this.brightness = brightness;
- this.context = pi4j;
-
- // Allocate SPI transmit buffer (same size as PCM)
- this.numLEDs = 0;
- for (int[] strips : matrix) {
- this.numLEDs += strips.length;
- }
-
- this.ledStrip = new LedStrip(pi4j, numLEDs, brightness, channel);
- this.spi = ledStrip.spi;
- }
-
- /**
- * @return the Pi4J context
- */
- public Context getContext() {
- return this.context;
- }
- /**
- * Setting all LEDs off and closing the strip
- */
- public void close() {
- ledStrip.close();
- }
-
- /**
- * function to get the amount of the LEDs in the matrix
- *
- * @return int with the amount of LEDs over all
- */
- public int getNumPixels() {
- return numLEDs;
- }
-
- /**
- * function to get the amount of the LEDs in the specified strip
- *
- * @return int with the amount of LEDs over all
- */
- public int getNumPixels(int strip) {
- if (strip > matrix.length || strip < 0) {
- throw new IllegalArgumentException("the strip specified does not exist");
- }
- return matrix[strip].length;
+ @Override
+ public void reset() {
+ super.reset();
+ ledStrip.reset();
}
/**
* function to get the color (as an int) of a specified LED
*
- * @param pixel which position on the LED strip, range 0 - numLEDS-1
+ * @param row row in the matrix, starting with 0
+ * @param column column in matrix, starting with 0
* @return the color of the specified LED on the strip
*/
- public int getPixelColor(int strip, int pixel) {
- if (strip > matrix.length || strip < 0 || pixel > matrix[strip].length || pixel < 0) {
- throw new IllegalArgumentException("the strip or led specified does not exist");
- }
- return matrix[strip][pixel];
+ public int getPixelColor(int row, int column) {
+ return ledStrip.getPixelColor(positionOnStrip(row, column));
}
+
/**
* setting the color of a specified led on the strip
*
- * @param pixel which position on the strip, range 0 - numLEDS-1
+ * @param row row in the matrix, starting with 0
+ * @param column column in matrix, starting with 0
* @param color the color that is set
*/
- public void setPixelColor(int strip, int pixel, int color) {
- if (strip > matrix.length || strip < 0 || pixel > matrix[strip].length || pixel < 0) {
- throw new IllegalArgumentException("the strip or LED specified does not exist");
- }
- matrix[strip][pixel] = color;
+ public void setPixelColor(int row, int column, int color) {
+ ledStrip.setPixelColor(positionOnStrip(row, column), color);
}
- /**
- * function to get the color (as an int) of a specified LED on the matrix
- *
- * @param pixelNumber which position in the matrix
- * if it was laid out like a single strip
- * @return the color of the specified led on the strip
- */
- public int getMatrixPixelColor(int pixelNumber) {
- if (pixelNumber > matrix.length || pixelNumber < 0) {
- throw new IllegalArgumentException("the strip or LED specified does not exist");
- }
- int counter = 0;
- int strip = 0;
- int LED = 0;
- while (counter < pixelNumber && strip < matrix.length) {
- LED = 0;
- while (counter < pixelNumber && LED < matrix[strip].length) {
- counter++;
- LED++;
- }
- if (counter < pixelNumber) strip++;
- }
- return matrix[strip][LED];
- }
/**
- * setting the color of a specified LED on the matrix
+ * Setting all LEDs of a row to the same color
*
- * @param pixelNumber which position in the matrix,
- * if it was laid out like a single strip
- * @param color the color that is set
+ * @param color the color that is set
*/
- public void setMatrixPixelColor(int pixelNumber, int color) {
- if (pixelNumber > matrix.length || pixelNumber < 0) {
- throw new IllegalArgumentException("the LED specified does not exist");
+ public void setRowColor(int row, int color) {
+ for(int i= (row * columns); i< (row * columns) + columns; i++){
+ ledStrip.setPixelColor(i, color);
}
- int counter = 0;
- int strip = 0;
- int LED = 0;
- while (counter < pixelNumber && strip < matrix.length) {
- LED = 0;
- while (counter < pixelNumber && LED < matrix[strip].length) {
- counter++;
- LED++;
- }
- if (counter < pixelNumber) strip++;
- }
- matrix[strip][LED] = color;
}
- /**
- * Setting all LEDs of a row to the same color
- *
- * @param color the color that is set
- */
- public void setStripColor(int strip, int color) {
- if (strip > matrix.length || strip < 0) {
- throw new IllegalArgumentException("the strip specified does not exist");
+ public void setColumnColor(int column, int color){
+ for(int i = 0; i
+ * In PI4J-OS-Image both SPI and UART are enabled and SPI uses bus#1 (if UART is disabled switch to GPIO#10 (SPI0 MOSI) and SPI bus#0.
*/
-public class LedStrip extends Component {
+public class LedStrip extends SpiDevice {
/**
* 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 int LED_RESET_WAIT_TIME = 300_000;
- /**
- * The PI4J SPI
- */
- protected final Spi spi;
+ private static final int DEFAULT_SPI_CHANNEL = 0;
/**
- * The PI4J context
+ * Default brightness
*/
- protected final Context context;
- /**
- * The amount of all LEDs
- */
- private final int numLEDs;
+ private static final float DEFAULT_BRIGHTNESS = 0.2f;
/**
* Default frequency of a WS2812 Neopixel Strip
*/
- private 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 int renderWaitTime;
+ private static final int DEFAULT_FREQUENCY_PI3 = 800_000; //use this for a Pi4
+ private static final int DEFAULT_FREQUENCY_PI4 = 500_000; //use this for a Pi4
/**
- * The array of all pixels
+ * the conversion from bit's of an integer to a byte
+ * we can write on the SPI
*/
- private final int[] LEDs;
+ 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
/**
- * The raw-data of all pixels, each int of LEDs is split into bits and converted to bytes to write
+ * The amount of LEDs
*/
- private final byte[] pixelRaw;
+ private final int numberOfLEDs;
+
/**
- * the conversion from bit's of an integer to a byte we can write on the SPI
+ * The array of all pixels
*/
- private final byte Bit_0 = (byte) 0b11000000;// 192 in Decimal
- private final byte Bit_1 = (byte) 0b11111000;// 248 in Decimal
- private final byte Bit_Reset = (byte) 0b00000000;// 0 in Decimal
+ private final int[] ledColors;
+
/**
* Brightness value between 0 and 1
*/
- private double brightness;
- /**
- * The time, when the last rendering happened
- */
- private long lastRenderTime;
+ private double maxBrightness;
/**
* 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
+ * @param numberOfLEDs How many LEDs are on this Strand
*/
- public LedStrip(Context pi4j, int numLEDs, double brightness) {
- this(pi4j, numLEDs, brightness, DEFAULT_SPI_CHANNEL);
+ public LedStrip(Context pi4j, int numberOfLEDs) {
+ this(pi4j, numberOfLEDs, DEFAULT_BRIGHTNESS, DEFAULT_SPI_CHANNEL);
}
/**
- * Creates a new simpleLed component with a custom BCM pin.
+ * Creates a new LedStrip component.
*
- * @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
+ * @param pi4j Pi4J context
+ * @param numberOfLEDs How many LEDs are on this strand
+ * @param maxBrightness How bright the leds can be at max, range 0 - 1
+ * @param channel which channel to use
*/
- public LedStrip(Context pi4j, int numLEDs, double brightness, int channel) {
- if (numLEDs < 1 || brightness < 0 || brightness > 1 || channel < 0 || channel > 1) {
+ public LedStrip(Context pi4j, int numberOfLEDs, double maxBrightness, int channel) {
+ super(pi4j,
+ Spi.newConfigBuilder(pi4j)
+ .id("SPI-" + channel)
+ .bus(SpiBus.BUS_1)
+ .name("LED Strip")
+ .address(channel)
+ .baud(8 * DEFAULT_FREQUENCY_PI4) //bit-banging from Bit to SPI-Byte
+ .build());
+ if (numberOfLEDs < 1 || maxBrightness < 0 || maxBrightness > 1 || channel < 0 || channel > 1) {
throw new IllegalArgumentException("Illegal Constructor");
}
- logDebug("initialising a LED strip 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 LED strip
- // 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 * 3 * 8 * 1250 + LED_RESET_WAIT_TIME;
+ this.numberOfLEDs = numberOfLEDs;
+ ledColors = new int[numberOfLEDs];
+
+ setMaxBrightness(0.01);
+ blink(LedColor.ORANGE, Duration.ofMillis(200), 2);
+
+ setMaxBrightness(maxBrightness);
+
+ logDebug("LED strip with %d LEDs", numberOfLEDs);
}
- /**
- * 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) //bit-banging from Bit to SPI-Byte
- .build();
+ public void blink(int color, Duration pulse, int times){
+ alternate(color, 0, pulse, times);
}
- /**
- * @return the Pi4J context
- */
- public Context getContext() {
- return this.context;
+ public void alternate(int firstColor, int secondColor, Duration pulse, int times){
+ for(int i=0; i
+ * In this implementation we use an 'Ads115' and attach the potentiometer to one of the ADC channels.
+ *
+ * We use the terms 'normalized value' and 'raw value'.
+ *
- * This leads to the following table for the maximum allowed readFrequency by a sampling rate of 128 sps:
- * 1 channel in use -> readFrequency max 64Hz (min. response time = 16ms)
- * 2 channel in use -> readFrequency max 32Hz (min. response time = 32ms)
- * 3 channel in use -> readFrequency max 21Hz (min. response time = 48ms)
- * 4 channel in use -> readFrequency max 16Hz (min. response time = 63ms)
- *
- * @param threshold threshold for trigger new value change event (+- voltage)
- * @param readFrequency read frequency to get new value from device, must be lower than 1/2
- * sampling rate of device
+ * @param onChange Event handler to call or null to disable
*/
- public void startSlowContinuousReading(double threshold, int readFrequency) {
- if (fastContinuousReadingActive) {
- throw new ContinuousMeasuringException("fast continuous reading currently active");
- } else {
- //set slow continuous reading active to lock fast continuous reading
- slowContinuousReadingActive = true;
- ads1115.startSlowContinuousReading(channel, threshold, readFrequency);
- }
+ public void onNormalizedValueChange(Consumer
+ * see GPS - NMEA sentence information
+ *
+ * There's nothing special about the GPS module. It just sends some data via the serial port
+ * We can use the standard 'SerialDevice' and configure it appropriately.
+ *
+ * SerialGps just converts the Strings delivered by SerialReader to Positions, consisting of longitude, latitude, altitude
+ *
+ */
+public class SerialGps extends Component {
+ //only if the sensor has moved significantly, the new position will be reported
+ private static final double MIN_DISTANCE_M = 1.0;
+
+ private final SerialDevice device;
+
+ private final Consumer
+ * For the delivered Strings see GPS - NMEA sentence information
+ *
+ * @param line the String delivered by the SerialReader
+ */
+ private void handleNewData(String line) {
+ logDebug("Serial reader delivered: '%s'", line);
+ String[] data = line.split(",");
+ switch (data[0]) {
+ case "$GPGGA" -> handleFixData(data);
+ case "$GPGLL" -> handlePosition(data[1], data[2], data[3], data[4]);
+ }
+ }
+
+ private void handleFixData(String[] data) {
+ try {
+ String satelliteString = data[7];
+ if(satelliteString.contains(".")){
+ satelliteString = satelliteString.substring(0, satelliteString.indexOf('.'));
+ }
+ numberOfSatellites = satelliteString.isEmpty() ? 0 : Integer.parseInt(satelliteString);
+ if(numberOfSatellites == 0){
+ logInfo("no satellites in view");
+ }
+ logDebug("Number of satellites in use: %d", numberOfSatellites);
+ handlePosition(data[2], data[3], data[4], data[5]);
+ handleAltitude(data[9]);
+ } catch (Exception e) {
+ logError("unknown NMEA sentence: '%s'", String.join(",", data));
+ }
+ }
+
+ private void handleAltitude(String altitudeString) {
+ if(numberOfSatellites >= 3 && onNewAltitude != null && !altitudeString.isEmpty()){
+ double altitude = Double.parseDouble(altitudeString);
+ if(Math.abs(altitude - lastReportedAltitude) >= MIN_DISTANCE_M){
+ lastReportedAltitude = altitude;
+ logDebug("Current altitude, %.1f m", altitude);
+ onNewAltitude.accept(altitude);
+ }
+ }
+ }
+
+ private void handlePosition(String lat, String northOrSouth, String lng, String eastOrWest){
+ if(numberOfSatellites >=3 && onNewPosition != null){
+ double latitude = 0;
+ if (!lat.isEmpty()) {
+ int degree = Integer.parseInt(lat.substring(0, 2));
+ double minutes = Double.parseDouble(lat.substring(2));
+ latitude = degree + (minutes / 60.0);
+ }
+ double longitude = 0;
+ if (!lng.isEmpty()) {
+ int degree = Integer.parseInt(lng.substring(0, 3));
+ double minutes = Double.parseDouble(lng.substring(3));
+ longitude = degree + (minutes / 60.0);
+ }
+ if (latitude != 0 && northOrSouth.equals("S")) {
+ latitude = -latitude;
+ }
+ if (longitude != 0 && eastOrWest.equals("W")) {
+ longitude = -longitude;
+ }
+
+ GeoPosition pos = new GeoPosition(latitude, longitude);
+
+ double moved = lastReportedPosition.distance(pos);
+ logDebug("Moved %.2f m", moved);
+ if(moved >= MIN_DISTANCE_M){
+ logDebug("GPS: new position: %s", pos.dms());
+ lastReportedPosition = pos;
+ onNewPosition.accept(pos);
+ }
+ else {
+ logDebug("No significant movement");
+ }
+ }
+
+ }
+
+ public record GeoPosition(double latitude, double longitude) {
+
+ public String dms() {
+ return format(latitude, longitude);
+ }
+
+ /**
+ * Just a simplified version of distance calculation. Doesn't take the different altitudes into account.
+ *
+ * @param otherPosition the position
+ * @return distance in meter
+ */
+ public double distance(GeoPosition otherPosition){
+ double lon1 = longitude;
+ double lon2 = otherPosition.longitude;
+ double lat1 = latitude;
+ double lat2 = otherPosition.latitude;
+ double theta = lon1 - lon2;
+
+ return rad2deg(Math.acos(Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta)))) * 60 * 1.1515 * 1609.344;
+ }
+
+ /**
+ * converts decimal degrees to radians
+ */
+ private double deg2rad(double deg) {
+ return (deg * Math.PI / 180.0);
+ }
+
+ /**
+ * converts radians to decimal degrees
+ */
+ private double rad2deg(double rad) {
+ return (rad * 180.0 / Math.PI);
+ }
+
+ private String format(double latitude, double longitude) {
+ String latCompassDirection = (latitude > 0.0) ? "N" : "S";
+ String lonCompassDirection = (longitude > 0.0) ? "E" : "W";
+
+ return String.format("%s%s, %s%s", getDMS(latitude), latCompassDirection, getDMS(longitude), lonCompassDirection);
+ }
+
+ private String getDMS(double value) {
+ double absValue = Math.abs(value);
+ int degree = (int) absValue;
+ int minutes = (int) ((absValue - degree) * 60.0);
+ double seconds = (absValue - degree - minutes / 60.0) * 3600.0;
+
+ return String.format("%d°%d′%s″", degree, minutes, String.format(Locale.ENGLISH, "%.4f", seconds));
+ }
+ }
+}
+
+
diff --git a/src/main/java/com/pi4j/catalog/components/SerialReader.java b/src/main/java/com/pi4j/catalog/components/SerialReader.java
deleted file mode 100644
index 22191af..0000000
--- a/src/main/java/com/pi4j/catalog/components/SerialReader.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.pi4j.catalog.components;
-
-import com.pi4j.io.serial.Serial;
-import com.pi4j.util.Console;
-
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-
-public class SerialReader implements Runnable {
-
- private final Console console;
- private final Serial serial;
-
- private boolean continueReading = true;
-
- public SerialReader(Console console, Serial serial) {
- this.console = console;
- this.serial = serial;
- }
-
- public void stopReading() {
- continueReading = false;
- }
-
- @Override
- public void run() {
- // We use a buffered reader to handle the data received from the serial port
- BufferedReader br = new BufferedReader(new InputStreamReader(serial.getInputStream()));
-
- try {
- // Data from the GPS is received in lines
- StringBuilder line = new StringBuilder();
-
- // Read data until the flag is false
- while (continueReading) {
- // First we need to check if there is data available to read.
- // The read() command for pi-gpio-serial is a NON-BLOCKING call, in contrast to typical java input streams.
- var available = serial.available();
- if (available > 0) {
- for (int i = 0; i < available; i++) {
- byte b = (byte) br.read();
- if (b < 32) {
- // All non-string bytes are handled as line breaks
- if (line.length() > 0) {
- // Here we should add code to parse the data to a GPS data object
- console.println("Data: '" + line + "'");
- line = new StringBuilder();
- }
- } else {
- line.append((char) b);
- }
- }
- } else {
- Thread.sleep(10);
- }
- }
- } catch (Exception e) {
- console.println("Error reading data from serial: " + e.getMessage());
- System.out.println(e.getStackTrace());
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/pi4j/catalog/components/ServoMotor.java b/src/main/java/com/pi4j/catalog/components/ServoMotor.java
index 2849d48..acc0a9c 100644
--- a/src/main/java/com/pi4j/catalog/components/ServoMotor.java
+++ b/src/main/java/com/pi4j/catalog/components/ServoMotor.java
@@ -1,11 +1,15 @@
package com.pi4j.catalog.components;
+import java.time.Duration;
+
import com.pi4j.context.Context;
-import com.pi4j.catalog.components.helpers.PIN;
import com.pi4j.io.pwm.Pwm;
-import com.pi4j.io.pwm.PwmConfig;
+import com.pi4j.io.pwm.PwmType;
+
+import com.pi4j.catalog.components.base.PIN;
+import com.pi4j.catalog.components.base.PwmActuator;
-public class ServoMotor extends Component {
+public class ServoMotor extends PwmActuator {
/**
* Default PWM frequency of the servo, based on values for SG92R
*/
@@ -29,11 +33,6 @@ public class ServoMotor extends Component {
*/
protected final static float DEFAULT_MAX_DUTY_CYCLE = 12;
- /**
- * Pi4J PWM instance for this servo
- */
- private final Pwm pwm;
-
/**
* Minimum angle of the servo motor used for this instance, should match previously tested real world values
*/
@@ -96,13 +95,29 @@ public ServoMotor(Context pi4j, PIN address, float minAngle, float maxAngle, flo
* @param maxDutyCycle Maximum duty cycle as float, between 0 and 100
*/
public ServoMotor(Context pi4j, PIN address, int frequency, float minAngle, float maxAngle, float minDutyCycle, float maxDutyCycle) {
- this.pwm = pi4j.create(buildPwmConfig(pi4j, address, frequency));
+ super(pi4j,
+ Pwm.newConfigBuilder(pi4j)
+ .id("BCM-" + address)
+ .name("Servo Motor " + address)
+ .address(address.getPin())
+ .pwmType(PwmType.HARDWARE)
+ .frequency(frequency)
+ .initial(0)
+ .shutdown(0)
+ .build());
this.minAngle = minAngle;
this.maxAngle = maxAngle;
this.minDutyCycle = minDutyCycle;
this.maxDutyCycle = maxDutyCycle;
}
+ @Override
+ public void reset() {
+ setAngle(0);
+ delay(Duration.ofSeconds(1));
+ super.reset();
+ }
+
/**
* Rotates the servo motor to the specified angle in degrees.
* The angle should be between {@link #getMinAngle()} and {@link #getMaxAngle()} which was specified during initialization.
@@ -208,7 +223,7 @@ private float mapToDutyCycle(float input, float inputStart, float inputEnd) {
* @param outputEnd Maximum value for output
* @return Mapped input value
*/
- private static float mapRange(float input, float inputStart, float inputEnd, float outputStart, float outputEnd) {
+ private float mapRange(float input, float inputStart, float inputEnd, float outputStart, float outputEnd) {
// Automatically swap minimum/maximum of input if inverted
if (inputStart > inputEnd) {
final float tmp = inputEnd;
@@ -225,41 +240,11 @@ private static float mapRange(float input, float inputStart, float inputEnd, flo
// Automatically clamp the input value and calculate the mapped value
final float clampedInput = Math.min(inputEnd, Math.max(inputStart, input));
- return outputStart + ((outputEnd - outputStart) / (inputEnd - inputStart)) * (clampedInput - inputStart);
- }
- /**
- * shutting down the component
- */
- public void off(){
- this.pwm.off();
- }
+ //this set the max to the left and min to the right
+ //return outputStart + ((outputEnd - outputStart) / (inputEnd - inputStart)) * (clampedInput - inputStart);
- /**
- * Returns the created PWM instance for the servo
- *
- * @return PWM instance
- */
- protected Pwm getPwm() {
- return pwm;
+ return outputEnd - ((outputEnd - outputStart) / (inputEnd - inputStart)) * (clampedInput - inputStart);
}
- /**
- * Builds a new PWM configuration for the step motor.
- *
- * @param pi4j Pi4J context
- * @param address BCM address
- * @param frequency PWM frequency
- * @return PWM configuration
- */
- protected PwmConfig buildPwmConfig(Context pi4j, PIN address, int frequency) {
- return Pwm.newConfigBuilder(pi4j)
- .id("BCM" + address)
- .name("Servo Motor")
- .address(address.getPin())
- .frequency(frequency)
- .initial(0)
- .shutdown(0)
- .build();
- }
}
diff --git a/src/main/java/com/pi4j/catalog/components/SimpleButton.java b/src/main/java/com/pi4j/catalog/components/SimpleButton.java
index a94ddb0..4384448 100644
--- a/src/main/java/com/pi4j/catalog/components/SimpleButton.java
+++ b/src/main/java/com/pi4j/catalog/components/SimpleButton.java
@@ -1,27 +1,29 @@
package com.pi4j.catalog.components;
-import com.pi4j.context.Context;
-import com.pi4j.catalog.components.helpers.PIN;
-import com.pi4j.io.gpio.digital.*;
-
+import java.time.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-public class SimpleButton extends Component {
- /**
- * Default debounce time in microseconds
- */
- private static final long DEFAULT_DEBOUNCE = 10_000;
+import com.pi4j.context.Context;
+import com.pi4j.io.gpio.digital.DigitalInput;
+import com.pi4j.io.gpio.digital.DigitalState;
+import com.pi4j.io.gpio.digital.PullResistance;
- /**
- * Pi4J digital input instance used by this component
- */
- private final DigitalInput digitalInput;
+import com.pi4j.catalog.components.base.DigitalSensor;
+import com.pi4j.catalog.components.base.PIN;
+
+import static com.pi4j.io.gpio.digital.DigitalInput.DEFAULT_DEBOUNCE;
+
+public class SimpleButton extends DigitalSensor {
/**
* Specifies if button state is inverted, e.g. HIGH = depressed, LOW = pressed
* This will also automatically switch the pull resistance to PULL_UP
*/
private final boolean inverted;
+ /**
+ * Runnable Code when button is depressed
+ */
+ private Runnable onUp;
/**
* Runnable Code when button is pressed
*/
@@ -29,27 +31,26 @@ public class SimpleButton extends Component {
/**
* Handler while button is pressed
*/
- private Runnable whilePressed;
+ private Runnable whileDown;
/**
* Timer while button is pressed
*/
- private long whilePressedDelay;
- /**
- * Runnable Code when button is depressed
- */
- private Runnable onUp;
+ private Duration whilePressedDelay;
+
/**
* what needs to be done while button is pressed (and whilePressed is != null)
*/
- private final Runnable whilePressedWorker = () -> {
+ private final Runnable whileDownWorker = () -> {
while (isDown()) {
delay(whilePressedDelay);
- if (isDown()) {
- whilePressed.run();
+ if (isDown() && whileDown != null) {
+ logDebug("whileDown triggered");
+ whileDown.run();
}
}
};
- private final ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ private ExecutorService executor;
/**
* Creates a new button component
@@ -69,9 +70,16 @@ public SimpleButton(Context pi4j, PIN address, boolean inverted) {
* @param debounce Debounce time in microseconds
*/
public SimpleButton(Context pi4j, PIN address, boolean inverted, long debounce) {
- this.inverted = inverted;
+ super(pi4j,
+ DigitalInput.newConfigBuilder(pi4j)
+ .id("BCM" + address)
+ .name("Button #" + address)
+ .address(address.getPin())
+ .debounce(debounce)
+ .pull(inverted ? PullResistance.PULL_UP : PullResistance.PULL_DOWN)
+ .build());
- this.digitalInput = pi4j.create(buildDigitalInputConfig(pi4j, address, inverted, debounce));
+ this.inverted = inverted;
/*
* Gets a DigitalStateChangeEvent directly from the Provider, as this
@@ -79,22 +87,24 @@ public SimpleButton(Context pi4j, PIN address, boolean inverted, long debounce)
* Calls the methods onUp, onDown and whilePressed. WhilePressed gets
* executed in an own Thread, as to not block other resources.
*/
- this.digitalInput.addListener(digitalStateChangeEvent -> {
+ digitalInput.addListener(digitalStateChangeEvent -> {
DigitalState state = getState();
- logDebug("Button switched to '" + state + "'");
+ logDebug("Button switched to '%s'", state);
switch (state) {
case HIGH -> {
if (onDown != null) {
+ logDebug("onDown triggered");
onDown.run();
}
- if (whilePressed != null) {
- executor.submit(whilePressedWorker);
+ if (whileDown != null) {
+ executor.submit(whileDownWorker);
}
}
case LOW -> {
if (onUp != null) {
+ logDebug("onUp triggered");
onUp.run();
}
}
@@ -104,22 +114,12 @@ public SimpleButton(Context pi4j, PIN address, boolean inverted, long debounce)
}
/**
- * Returns the current state of the Digital State
+ * Checks if button is currently pressed.
+ *
+ * For a not-inverted button this means: if the button is pressed, then full voltage is present
+ * at the GPIO-Pin. Therefore, the DigitalState is HIGH
*
- * @return Current DigitalInput state (Can be HIGH, LOW or UNKNOWN)
- */
- public DigitalState getState() {
- return switch (digitalInput.state()) {
- case HIGH -> inverted ? DigitalState.LOW : DigitalState.HIGH;
- case LOW -> inverted ? DigitalState.HIGH : DigitalState.LOW;
- default -> DigitalState.UNKNOWN;
- };
- }
-
- /**
- * Checks if button is currently pressed
- *
- * @return True if button is pressed
+ * @return true if button is pressed
*/
public boolean isDown() {
return getState() == DigitalState.HIGH;
@@ -127,108 +127,89 @@ public boolean isDown() {
/**
* Checks if button is currently depressed (= NOT pressed)
+ *
+ * For a not-inverted button this means: if the button is depressed, then no voltage is present
+ * at the GPIO-Pin. Therefore, the DigitalState is LOW
*
- * @return True if button is depressed
+ * @return true if button is depressed
*/
public boolean isUp() {
return getState() == DigitalState.LOW;
}
- /**
- * Returns the Pi4J DigitalInput associated with this component.
- *
- * @return Returns the Pi4J DigitalInput associated with this component.
- */
- public DigitalInput getDigitalInput() {
- return this.digitalInput;
- }
-
- /**
- * Builds a new DigitalInput configuration for the button component.
- *
- * @param pi4j Pi4J context
- * @param address GPIO address of button component
- * @param inverted Specify if button state is inverted
- * @param debounce Debounce time in microseconds
- * @return DigitalInput configuration
- */
- private DigitalInputConfig buildDigitalInputConfig(Context pi4j, PIN address, boolean inverted, long debounce) {
- return DigitalInput.newConfigBuilder(pi4j).id("BCM" + address)
- .name("Button #" + address)
- .address(address.getPin())
- .debounce(debounce).pull(inverted ? PullResistance.PULL_UP : PullResistance.PULL_DOWN)
- .build();
- }
-
/**
* Sets or disables the handler for the onDown event.
+ *
* This event gets triggered whenever the button is pressed.
* Only a single event handler can be registered at once.
*
* @param task Event handler to call or null to disable
*/
public void onDown(Runnable task) {
- this.onDown = task;
+ onDown = task;
}
/**
* Sets or disables the handler for the onUp event.
+ *
* This event gets triggered whenever the button is no longer pressed.
* Only a single event handler can be registered at once.
*
* @param task Event handler to call or null to disable
*/
public void onUp(Runnable task) {
- this.onUp = task;
+ onUp = task;
}
/**
* Sets or disables the handler for the whilePressed event.
+ *
* This event gets triggered whenever the button is pressed.
* Only a single event handler can be registered at once.
*
* @param task Event handler to call or null to disable
*/
- public void whilePressed(Runnable task, long whilePressedDelay) {
- this.whilePressed = task;
- this.whilePressedDelay = whilePressedDelay;
+ public void whilePressed(Runnable task, Duration delay) {
+ whileDown = task;
+ whilePressedDelay = delay;
+ if(executor != null){
+ executor.shutdownNow();
+ }
+ if(task != null){
+ executor = Executors.newSingleThreadExecutor();
+ }
}
- /**
- * disables all the handlers for the onUp, onDown and WhilePressed Events
- */
- public void deRegisterAll() {
- this.onDown = null;
- this.onUp = null;
- this.whilePressed = null;
- this.executor.shutdown();
+ public boolean isInInitialState(){
+ return onDown == null && onUp == null && whileDown == null && executor == null;
}
/**
- * Returns the methode for OnDown
- *
- * @return Runnable onDown
+ * disables all the handlers for the onUp, onDown and WhileDown Events
*/
- public Runnable getOnDown() {
- return onDown;
+ @Override
+ public void reset() {
+ onDown = null;
+ onUp = null;
+ whileDown = null;
+ if(executor != null){
+ executor.shutdown();
+ }
+ executor = null;
}
/**
- * Returns the methode for OnUp
+ * Returns the current state of the Digital State
*
- * @return Runnable onUp
+ * @return Current DigitalInput state (Can be HIGH, LOW or UNKNOWN)
*/
- public Runnable getOnUp() {
- return onUp;
+ private DigitalState getState() {
+ return switch (digitalInput.state()) {
+ case HIGH -> inverted ? DigitalState.LOW : DigitalState.HIGH;
+ case LOW -> inverted ? DigitalState.HIGH : DigitalState.LOW;
+ default -> DigitalState.UNKNOWN;
+ };
}
- /**
- * Returns the methode for whilePressed
- *
- * @return Runnable whilePressed
- */
- public Runnable getWhilePressed() {
- return whilePressed;
- }
}
diff --git a/src/main/java/com/pi4j/catalog/components/SimpleLed.java b/src/main/java/com/pi4j/catalog/components/SimpleLed.java
index e4fc12a..56f5cde 100644
--- a/src/main/java/com/pi4j/catalog/components/SimpleLed.java
+++ b/src/main/java/com/pi4j/catalog/components/SimpleLed.java
@@ -1,46 +1,46 @@
package com.pi4j.catalog.components;
import com.pi4j.context.Context;
-import com.pi4j.catalog.components.helpers.PIN;
import com.pi4j.io.gpio.digital.DigitalOutput;
-import com.pi4j.io.gpio.digital.DigitalOutputConfig;
-public class SimpleLed extends Component {
- /**
- * Pi4J digital output instance used by this component
- */
- private final DigitalOutput digitalOutput;
+import com.pi4j.catalog.components.base.DigitalActuator;
+import com.pi4j.catalog.components.base.PIN;
+
+public class SimpleLed extends DigitalActuator {
/**
- * Creates a new simpleLed component with a custom BCM pin.
+ * Creates a new SimpleLed component with a custom BCM pin.
*
* @param pi4j Pi4J context
* @param address Custom BCM pin address
*/
public SimpleLed(Context pi4j, PIN address) {
- this.digitalOutput = pi4j.create(buildDigitalOutputConfig(pi4j, address));
- }
-
- /**
- * Set the LED on or off depending on the boolean argument.
- *
- * @param on Sets the LED to on (true) or off (false)
- */
- public void setState(boolean on) {
- digitalOutput.setState(on);
+ super(pi4j,
+ DigitalOutput.newConfigBuilder(pi4j)
+ .id("BCM" + address)
+ .name("LED #" + address)
+ .address(address.getPin())
+ .build());
+ logDebug("Created new SimpleLed component");
}
/**
* Sets the LED to on.
*/
public void on() {
+ logDebug("LED turned ON");
digitalOutput.on();
}
+ public boolean isOn(){
+ return digitalOutput.isOn();
+ }
+
/**
* Sets the LED to off
*/
public void off() {
+ logDebug("LED turned OFF");
digitalOutput.off();
}
@@ -49,32 +49,15 @@ public void off() {
*
* @return Return true or false according to the new state of the relay.
*/
- public boolean toggleState() {
+ public boolean toggle() {
digitalOutput.toggle();
- return digitalOutput.isOff();
- }
+ logDebug("LED toggled, now it is %s", digitalOutput.isOff() ? "OFF" : "ON");
- /**
- * Returns the instance of the digital output
- *
- * @return DigitalOutput instance of the LED
- */
- public DigitalOutput getDigitalOutput() {
- return digitalOutput;
+ return digitalOutput.isOff();
}
- /**
- * Configure Digital Output
- *
- * @param pi4j PI4J Context
- * @param address GPIO Address of the relay
- * @return Return Digital Output configuration
- */
- protected DigitalOutputConfig buildDigitalOutputConfig(Context pi4j, PIN address) {
- return DigitalOutput.newConfigBuilder(pi4j)
- .id("BCM" + address)
- .name("LED")
- .address(address.getPin())
- .build();
+ @Override
+ public void reset() {
+ off();
}
}
diff --git a/src/main/java/com/pi4j/catalog/components/base/Component.java b/src/main/java/com/pi4j/catalog/components/base/Component.java
new file mode 100644
index 0000000..a36cf71
--- /dev/null
+++ b/src/main/java/com/pi4j/catalog/components/base/Component.java
@@ -0,0 +1,74 @@
+package com.pi4j.catalog.components.base;
+
+import java.time.Duration;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public abstract class Component {
+ /**
+ * Logger instance
+ */
+ private static final Logger logger = Logger.getLogger("Pi4J Components");
+
+ static {
+ Level appropriateLevel = Level.INFO;
+ //Level appropriateLevel = Level.FINE; //use if 'debug'
+
+ System.setProperty("java.util.logging.SimpleFormatter.format",
+ "%4$s: %5$s [%1$tl:%1$tM:%1$tS %1$Tp]%n");
+
+ logger.setLevel(appropriateLevel);
+ logger.setUseParentHandlers(false);
+ ConsoleHandler handler = new ConsoleHandler();
+ handler.setLevel(appropriateLevel);
+ logger.addHandler(handler);
+ }
+
+ protected Component(){
+ }
+
+ /**
+ * Override this method to clean up all used resources
+ */
+ public void reset(){
+ //nothing to do by default
+ }
+
+ protected void logInfo(String msg, Object... args) {
+ logger.info(() -> String.format(msg, args));
+ }
+
+ protected void logError(String msg, Object... args) {
+ logger.severe(() -> String.format(msg, args));
+ }
+
+ protected void logDebug(String msg, Object... args) {
+ logger.fine(() -> String.format(msg, args));
+ }
+
+ protected void logException(String msg, Throwable exception){
+ logger.log(Level.SEVERE, msg, exception);
+ }
+
+ /**
+ * Utility function to sleep for the specified amount of milliseconds.
+ * An {@link InterruptedException} will be caught and ignored while setting the interrupt flag again.
+ *
+ * @param duration Time to sleep
+ */
+ protected void delay(Duration duration) {
+ try {
+ long nanos = duration.toNanos();
+ long millis = nanos / 1_000_000;
+ int remainingNanos = (int) (nanos % 1_000_000);
+ Thread.currentThread().sleep(millis, remainingNanos);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ protected
+ *
+ *
+ */
public class JoystickAnalog extends Component {
+
+ private final Ads1115 ads1115;
/**
* potentiometer x axis
*/
- private final Potentiometer x;
+ private final Potentiometer xAxis;
/**
* potentiometer y axis
*/
- private final Potentiometer y;
+ private final Potentiometer yAxis;
/**
* button push
+ *
+ * Can be 'null' if joystick doesn't have button functionality or if you don't want to use it
*/
private final SimpleButton push;
- /**
- * default channel for potentiometer x-axis
- */
- private static final int DEFAULT_CHANNEL_POTENTIOMETER_X = 0;
- /**
- * default channel for potentiometer x-axis
- */
- private static final int DEFAULT_CHANNEL_POTENTIOMETER_Y = 1;
- /**
- * default max voltage for raspberry pi
- */
- private static final double DEFAULT_MAX_VOLTAGE = 3.3;
- /**
- * default normalization if true -> normalization from 0 to 1
- * if false -> normalization from -1 to 1
- */
- private static final boolean DEFAULT_NORMALIZATION = true;
- /**
- * normalized center position
- */
- private final double NORMALIZED_CENTER_POSITION = 0.5;
- /**
- * offset center x-axis
- */
- private double xOffset = 0.0;
- /**
- * offset center y-axis
- */
- private double yOffset = 0.0;
- /**
- * if true normalized axis from 0 to 1 center is 0.5, if false normalized axis from -1 to 1 center is 0
- */
- private final boolean normalized0to1;
- /**
- * minimal normalized value on x axis
- */
- private double xMinNormValue;
- /**
- * maximal normalized value on x axis
- */
- private double xMaxNormValue;
- /**
- * minimal normalized value on y axis
- */
- private double yMinNormValue;
- /**
- * maximal normalized value on y axis
- */
- private double yMaxNormValue;
- /**
- * Builds a new JoystickAnalog component with default configuration for raspberry pi with ads1115 object
- *
- * @param pi4j Pi4J context
- * @param ads1115 ads object
- * @param push additional push button on joystick
- */
- public JoystickAnalog(Context pi4j, Ads1115 ads1115, PIN push) {
- this(pi4j, ads1115, DEFAULT_CHANNEL_POTENTIOMETER_X, DEFAULT_CHANNEL_POTENTIOMETER_Y, DEFAULT_MAX_VOLTAGE, DEFAULT_NORMALIZATION, push);
- }
+ private final double normThreshold;
+
+ private double xActualValue;
+ private double yActualValue;
+
+ private double xLastNotifiedValue = 999;
+ private double yLastNotifiedValue = 999;
+
/**
* Builds a new JoystickAnalog component with custom input for x-, y-axis, custom pin for push button.
* ads component needs to be created outside this clas, other channels may be used for other components.
*
- * @param pi4j Pi4J context
- * @param ads1115 ads object
- * @param chanelXAxis analog potentiometer x-axis
- * @param chanelYAxis analog potentiometer y-axis
- * @param maxVoltage max voltage expects on analog input x- and y-axis
- * @param normalized0to1 normalization axis if true -> normalization from 0 to 1 if false -> normalization from -1 to 1
- * @param push additional push button on joystick
- */
- public JoystickAnalog(Context pi4j, Ads1115 ads1115, int chanelXAxis, int chanelYAxis, double maxVoltage, boolean normalized0to1, PIN push) {
- this(new Potentiometer(ads1115, chanelXAxis, maxVoltage), new Potentiometer(ads1115, chanelYAxis, maxVoltage), normalized0to1, new SimpleButton(pi4j, push, true));
+ * @param ads1115 ads object
+ * @param channelXAxis analog potentiometer x-axis
+ * @param channelYAxis analog potentiometer y-axis
+ * @param pin additional push button on joystick
+ */
+ public JoystickAnalog(Ads1115 ads1115, Ads1115.Channel channelXAxis, Ads1115.Channel channelYAxis, PIN pin, boolean inverted) {
+ this(ads1115,
+ new Potentiometer(ads1115, channelXAxis, Potentiometer.Range.MINUS_ONE_TO_ONE),
+ new Potentiometer(ads1115, channelYAxis, Potentiometer.Range.MINUS_ONE_TO_ONE),
+ 0.05,
+ pin != null ? new SimpleButton(ads1115.getPi4j(), pin, inverted) : null);
}
/**
* @param potentiometerX potentiometer object for x-axis
* @param potentiometerY potentiometer object for y-axis
- * @param normalized0to1 normalization axis if true -> normalization from 0 to 1 if false -> normalization from -1 to 1
* @param push simpleButton object for push button on joystick
*/
- public JoystickAnalog(Potentiometer potentiometerX, Potentiometer potentiometerY, boolean normalized0to1, SimpleButton push) {
- this.x = potentiometerX;
- this.y = potentiometerY;
- this.push = push;
- this.normalized0to1 = normalized0to1;
-
- xMinNormValue = 0.1;
- xMaxNormValue = 0.9;
- yMinNormValue = 0.1;
- yMaxNormValue = 0.9;
+ JoystickAnalog(Ads1115 ads1115, Potentiometer potentiometerX, Potentiometer potentiometerY, double normThreshold, SimpleButton push) {
+ this.ads1115 = ads1115;
+ this.xAxis = potentiometerX;
+ this.yAxis = potentiometerY;
+ this.push = push;
+ this.normThreshold = normThreshold;
}
- /**
- * Sets or disables the handler for a value change event.
- * This event gets triggered whenever the x-axis of the joystick is moving.
- * Only a single event handler can be registered at once.
- *
- * @param task Event handler to call or null to disable
- */
- public void xOnMove(Consumer
+ *
+ */
public class Potentiometer extends Component {
+ public enum Range {
+ ZERO_TO_ONE, MINUS_ONE_TO_ONE
+ }
+
/**
* ads1115 instance
*/
private final Ads1115 ads1115;
+ private final Range range;
/**
- * AD channel connected to potentiometer (must be between 0 and 3)
- */
- private final int channel;
- /**
- * min value which potentiometer has reached
- */
- private double minValue;
-
- /**
- * max value which potentiometer has reached
- */
- private double maxValue;
-
- /**
- * fast continuous reading is active
- */
- private boolean fastContinuousReadingActive = false;
-
- /**
- * slow continuous reading is active
+ * ADC channel the potentiometer is attached to
*/
- private boolean slowContinuousReadingActive = false;
+ private final Ads1115.Channel channel;
/**
- * Create a new potentiometer component with custom channel and custom maxVoltage
+ * Create a new potentiometer component attached to the specified ADC channel and preliminary values for raw value range
*
- * @param ads1115 ads instance
- * @param channel custom ad channel
- * @param maxVoltage custom maxVoltage
+ * @param ads1115 ADC instance
*/
- public Potentiometer(Ads1115 ads1115, int channel, double maxVoltage) {
- this.ads1115 = ads1115;
- this.minValue = ads1115.getPga().gain() * 0.1;
- this.maxValue = maxVoltage;
- this.channel = channel;
-
- //check if channel is in range of ad converter
- if (channel < 0 || channel > 3) {
- throw new ConfigException("Channel number for ad converter not possible, choose channel between 0 to 3");
- }
- logDebug("Build component potentiometer");
+ public Potentiometer(Ads1115 ads1115, Ads1115.Channel channel) {
+ this(ads1115, channel, Range.ZERO_TO_ONE);
}
- /**
- * Create a new potentiometer component with default channel and maxVoltage for Raspberry pi
- *
- * @param ads1115 ads instance
- */
- public Potentiometer(Ads1115 ads1115) {
+ public Potentiometer(Ads1115 ads1115, Ads1115.Channel channel, Range range) {
this.ads1115 = ads1115;
- this.minValue = ads1115.getPga().gain() * 0.1;
- this.maxValue = 3.3;
- this.channel = 0;
+ this.range = range;
+ this.channel = channel;
- logDebug("Build component potentiometer");
+ logDebug("Potentiometer initialized");
}
/**
@@ -74,16 +55,8 @@ public Potentiometer(Ads1115 ads1115) {
*
* @return voltage from potentiometer
*/
- public double singleShotGetVoltage() {
- double result = switch (channel) {
- case 0 -> ads1115.singleShotAIn0();
- case 1 -> ads1115.singleShotAIn1();
- case 2 -> ads1115.singleShotAIn2();
- case 3 -> ads1115.singleShotAIn3();
- default -> 0.0;
- };
- updateMinMaxValue(result);
- return result;
+ public double readCurrentVoltage() {
+ return ads1115.readValue(channel);
}
/**
@@ -91,23 +64,8 @@ public double singleShotGetVoltage() {
*
* @return normalized value
*/
- public double singleShotGetNormalizedValue() {
- return singleShotGetVoltage() / maxValue;
- }
-
- /**
- * Sets or disables the handler for the onValueChange event.
- * This event gets triggered whenever the value changes.
- * Only a single event handler can be registered at once.
- *
- * @param method Event handler to call or null to disable
- */
- public void setConsumerFastRead(Consumer