first commit
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import gpio.NanopiGpio;
|
||||
import mqtt.MqttClient;
|
||||
import org.tinylog.Logger;
|
||||
|
||||
@@ -6,9 +7,39 @@ import org.tinylog.Logger;
|
||||
public class Main {
|
||||
private static config config;
|
||||
private static MqttClient mqttClient;
|
||||
private static NanopiGpio gpio;
|
||||
|
||||
// Application entry point
|
||||
public static void main(String[] args) {
|
||||
Logger.info("Application started");
|
||||
config = new config();
|
||||
|
||||
// Initialize the GPIO pins
|
||||
gpio = new NanopiGpio();
|
||||
// cek di https://wiki.friendlyelec.com/wiki/index.php/NanoPi_Duo20
|
||||
gpio.Open(opened -> {
|
||||
if (opened) {
|
||||
Logger.info("GPIO pins opened successfully.");
|
||||
} else {
|
||||
Logger.error("GPIO pins closed or failed to open.");
|
||||
}
|
||||
}, pinStatus -> {
|
||||
// TODO Handle pin status updates here
|
||||
Logger.info("Gpio {}, Description {}, status updated to {}", pinStatus.getGpioNumber(), pinStatus.getDescription(), pinStatus.getStatus());
|
||||
|
||||
|
||||
},
|
||||
new gpio.PinInfo(11, "Relay 1"),
|
||||
new gpio.PinInfo(12, "Relay 2"),
|
||||
new gpio.PinInfo(13, "Relay 3"),
|
||||
new gpio.PinInfo(14, "Relay 4"),
|
||||
new gpio.PinInfo(16, "Relay 5"),
|
||||
new gpio.PinInfo(15, "Relay 6"),
|
||||
new gpio.PinInfo(199, "Relay 7"),
|
||||
new gpio.PinInfo(198, "Relay 8")
|
||||
);
|
||||
|
||||
// initialize the MQTT client
|
||||
mqttClient = new MqttClient(config.getMQTT_Broker(), config.getMQTT_Port(), config.getMQTT_Username(), config.getMQTT_Password(), connected -> {
|
||||
if (connected) {
|
||||
Logger.info("MQTT client connected successfully.");
|
||||
@@ -23,5 +54,10 @@ public class Main {
|
||||
Logger.error("Failed to connect MQTT client.");
|
||||
}
|
||||
});
|
||||
|
||||
// Add a shutdown hook to disconnect all services
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
if (mqttClient!=null && mqttClient.isConnected()) mqttClient.Disconnect();
|
||||
}));
|
||||
}
|
||||
}
|
||||
9
src/gpio/Gpio.java
Normal file
9
src/gpio/Gpio.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package gpio;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface Gpio {
|
||||
void Open(Consumer<Boolean> callback, Consumer<PinStatus> pinStatus, PinInfo... pinInfos);
|
||||
void Close();
|
||||
boolean IsOpened();
|
||||
}
|
||||
123
src/gpio/NanopiGpio.java
Normal file
123
src/gpio/NanopiGpio.java
Normal file
@@ -0,0 +1,123 @@
|
||||
package gpio;
|
||||
|
||||
|
||||
import org.tinylog.Logger;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class NanopiGpio implements Gpio {
|
||||
private boolean isopened = false;
|
||||
private final Path gpioPath = Path.of("/sys/class/gpio") ;
|
||||
private final Path gpioExportPath = gpioPath.resolve( "export" ) ;
|
||||
private final Path gpioUnexportPath = gpioPath.resolve( "unexport" ) ;
|
||||
private final boolean IsLinux = System.getProperty("os.name").toLowerCase().contains("linux");
|
||||
private final List<PinInfo> openedPins;
|
||||
|
||||
public NanopiGpio(){
|
||||
openedPins = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean IsOpened() {
|
||||
return isopened;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({"BusyWait"})
|
||||
@Override
|
||||
public void Open(Consumer<Boolean> OpenedCallback, Consumer<PinStatus> pinStatus, PinInfo... pinInfos) {
|
||||
if (IsLinux){
|
||||
if (Files.exists(gpioExportPath) && Files.exists(gpioUnexportPath)){
|
||||
if (pinInfos!=null && pinInfos.length>0) {
|
||||
openedPins.clear();
|
||||
for(PinInfo pinInfo : pinInfos){
|
||||
try{
|
||||
Files.writeString(gpioExportPath, String.valueOf(pinInfo.GpioNumber()));
|
||||
Logger.info("Exported GPIO pin: {}", pinInfo.GpioNumber());
|
||||
openedPins.add(pinInfo);
|
||||
} catch (Exception ex){
|
||||
Logger.error("Error while processing GPIO pin {}: {}", pinInfo.GpioNumber(), ex.getMessage());
|
||||
}
|
||||
}
|
||||
if (openedPins.size()==pinInfos.length){
|
||||
isopened = true;
|
||||
|
||||
|
||||
Thread tx = new Thread(() -> {
|
||||
List<PinStatus> pinStatusList = new ArrayList<>();
|
||||
for (PinInfo pinInfo : openedPins) {
|
||||
pinStatusList.add(new PinStatus(pinInfo.GpioNumber(), pinInfo.Description(), -1));
|
||||
}
|
||||
if (OpenedCallback != null) OpenedCallback.accept(true);
|
||||
Logger.info("Gpio Scanner started");
|
||||
while (isopened){
|
||||
try{
|
||||
Thread.sleep(20);
|
||||
for(PinStatus status : pinStatusList){
|
||||
try {
|
||||
String value = Files.readString(gpioPath.resolve("gpio" + status.getGpioNumber() + "/value")).trim();
|
||||
if (value.equals("1")) {
|
||||
if (status.getStatus()!=1){
|
||||
status.setStatus(1);
|
||||
if (pinStatus != null) {
|
||||
pinStatus.accept(status); // Notify the callback with the updated status
|
||||
}
|
||||
}
|
||||
} else if (value.equals("0")) {
|
||||
if (status.getStatus()!=0){
|
||||
status.setStatus(0);
|
||||
if (pinStatus != null) {
|
||||
pinStatus.accept(status); // Notify the callback with the updated status
|
||||
}
|
||||
}
|
||||
} else {
|
||||
status.setStatus(-1); // Set status to -1 if value is not 0 or 1
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.error("Error reading GPIO pin {}: {}", status.getGpioNumber(), e.getMessage());
|
||||
status.setStatus(-1); // Set status to -1 on error
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ignored){}
|
||||
|
||||
}
|
||||
Logger.info("Gpio Scanner stopped");
|
||||
if (OpenedCallback!=null) OpenedCallback.accept(false);
|
||||
});
|
||||
tx.setName("GpioScanner");
|
||||
tx.setDaemon(true);
|
||||
tx.start();
|
||||
return;
|
||||
}
|
||||
|
||||
} else Logger.error("Nothing to open, no pinInfos provided.");
|
||||
} else Logger.error("GPIO paths do not exist: {} or {}", gpioExportPath, gpioUnexportPath);
|
||||
} else Logger.error("This GPIO implementation is only supported on Linux systems.");
|
||||
|
||||
if (OpenedCallback!=null) OpenedCallback.accept(false);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void Close() {
|
||||
isopened = false;
|
||||
if (!openedPins.isEmpty()){
|
||||
openedPins.forEach(p -> {
|
||||
try {
|
||||
Files.writeString(gpioUnexportPath, String.valueOf(p.GpioNumber()));
|
||||
Logger.info("Unexported GPIO pin: {}", p.GpioNumber());
|
||||
} catch (Exception e) {
|
||||
Logger.error("Error while unexporting GPIO pin {}: {}", p.GpioNumber(), e.getMessage());
|
||||
}
|
||||
});
|
||||
openedPins.clear();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
4
src/gpio/PinInfo.java
Normal file
4
src/gpio/PinInfo.java
Normal file
@@ -0,0 +1,4 @@
|
||||
package gpio;
|
||||
|
||||
public record PinInfo(int GpioNumber, String Description) {
|
||||
}
|
||||
17
src/gpio/PinStatus.java
Normal file
17
src/gpio/PinStatus.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package gpio;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
public class PinStatus {
|
||||
private final int GpioNumber;
|
||||
private final String Description;
|
||||
private @Setter int Status;
|
||||
public PinStatus(int GpioNumber, String Description, int status) {
|
||||
this.Description = Description;
|
||||
this.GpioNumber = GpioNumber;
|
||||
this.Status = status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package mqtt;
|
||||
import com.hivemq.client.mqtt.mqtt3.Mqtt3AsyncClient;
|
||||
import com.hivemq.client.mqtt.mqtt3.Mqtt3Client;
|
||||
import com.hivemq.client.mqtt.mqtt3.message.auth.Mqtt3SimpleAuth;
|
||||
import lombok.Getter;
|
||||
import org.tinylog.Logger;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -11,6 +12,7 @@ import java.util.function.Consumer;
|
||||
public class MqttClient {
|
||||
|
||||
Mqtt3AsyncClient client;
|
||||
private @Getter boolean connected = false;
|
||||
|
||||
/**
|
||||
* Constructor to create an MQTT client and connect to the broker.
|
||||
@@ -22,6 +24,7 @@ public class MqttClient {
|
||||
* @param connectedCallback A callback that is called when the connection is established or fails.
|
||||
*/
|
||||
public MqttClient(String host, int port, String username, String password, Consumer<Boolean> connectedCallback) {
|
||||
|
||||
Mqtt3AsyncClient clientx = Mqtt3Client.builder()
|
||||
.simpleAuth(Mqtt3SimpleAuth.builder().username(username).password(password.getBytes(StandardCharsets.UTF_8)).build())
|
||||
.serverHost(host)
|
||||
@@ -42,10 +45,26 @@ public class MqttClient {
|
||||
if (connectedCallback != null) {
|
||||
connectedCallback.accept(true);
|
||||
}
|
||||
connected = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Disconnect(){
|
||||
if (client!=null){
|
||||
client.disconnect()
|
||||
.whenComplete((disconn, throwable) -> {
|
||||
if (throwable != null) {
|
||||
Logger.error("Failed to disconnect from MQTT broker, Message : {}", throwable.getMessage());
|
||||
} else {
|
||||
Logger.info("Disconnected from MQTT broker.");
|
||||
}
|
||||
connected = false;
|
||||
client = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Publish(String topic, String clientID, String message, Consumer<Boolean> publishCallback) {
|
||||
if (client == null) {
|
||||
Logger.error("Cannot publish message, MQTT client is not connected.");
|
||||
|
||||
Reference in New Issue
Block a user