first commit
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import gpio.NanopiGpio;
|
||||||
import mqtt.MqttClient;
|
import mqtt.MqttClient;
|
||||||
import org.tinylog.Logger;
|
import org.tinylog.Logger;
|
||||||
|
|
||||||
@@ -6,9 +7,39 @@ import org.tinylog.Logger;
|
|||||||
public class Main {
|
public class Main {
|
||||||
private static config config;
|
private static config config;
|
||||||
private static MqttClient mqttClient;
|
private static MqttClient mqttClient;
|
||||||
|
private static NanopiGpio gpio;
|
||||||
|
|
||||||
|
// Application entry point
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
Logger.info("Application started");
|
Logger.info("Application started");
|
||||||
config = new config();
|
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 -> {
|
mqttClient = new MqttClient(config.getMQTT_Broker(), config.getMQTT_Port(), config.getMQTT_Username(), config.getMQTT_Password(), connected -> {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
Logger.info("MQTT client connected successfully.");
|
Logger.info("MQTT client connected successfully.");
|
||||||
@@ -23,5 +54,10 @@ public class Main {
|
|||||||
Logger.error("Failed to connect MQTT client.");
|
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.Mqtt3AsyncClient;
|
||||||
import com.hivemq.client.mqtt.mqtt3.Mqtt3Client;
|
import com.hivemq.client.mqtt.mqtt3.Mqtt3Client;
|
||||||
import com.hivemq.client.mqtt.mqtt3.message.auth.Mqtt3SimpleAuth;
|
import com.hivemq.client.mqtt.mqtt3.message.auth.Mqtt3SimpleAuth;
|
||||||
|
import lombok.Getter;
|
||||||
import org.tinylog.Logger;
|
import org.tinylog.Logger;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@@ -11,6 +12,7 @@ import java.util.function.Consumer;
|
|||||||
public class MqttClient {
|
public class MqttClient {
|
||||||
|
|
||||||
Mqtt3AsyncClient client;
|
Mqtt3AsyncClient client;
|
||||||
|
private @Getter boolean connected = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor to create an MQTT client and connect to the broker.
|
* 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.
|
* @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) {
|
public MqttClient(String host, int port, String username, String password, Consumer<Boolean> connectedCallback) {
|
||||||
|
|
||||||
Mqtt3AsyncClient clientx = Mqtt3Client.builder()
|
Mqtt3AsyncClient clientx = Mqtt3Client.builder()
|
||||||
.simpleAuth(Mqtt3SimpleAuth.builder().username(username).password(password.getBytes(StandardCharsets.UTF_8)).build())
|
.simpleAuth(Mqtt3SimpleAuth.builder().username(username).password(password.getBytes(StandardCharsets.UTF_8)).build())
|
||||||
.serverHost(host)
|
.serverHost(host)
|
||||||
@@ -42,10 +45,26 @@ public class MqttClient {
|
|||||||
if (connectedCallback != null) {
|
if (connectedCallback != null) {
|
||||||
connectedCallback.accept(true);
|
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) {
|
public void Publish(String topic, String clientID, String message, Consumer<Boolean> publishCallback) {
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
Logger.error("Cannot publish message, MQTT client is not connected.");
|
Logger.error("Cannot publish message, MQTT client is not connected.");
|
||||||
|
|||||||
Reference in New Issue
Block a user