Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions sensors/others/sensorhub-driver-meshtastic/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ plugins {

description = 'Meshtastic Driver'
ext.details = "Driver that utilizes a meshtastic Radio pre-configured with meshtastic Node Software to communicate with other meshtastic Radio"
version = '1.0.0'
version = '1.0.1'


dependencies {
implementation 'org.sensorhub:sensorhub-core:' + oshCoreVersion
embeddedImpl "com.google.protobuf:protobuf-java:3.25.1"
embeddedImpl "com.google.protobuf:protobuf-java:4.28.2"
testImplementation "junit:junit:4.13.1"
testImplementation project(':sensorhub-comm-jssc')
}

// PROTOBUF PLUGIN CONFIGURATION
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.25.1"
artifact = "com.google.protobuf:protoc:4.28.2"
}
// CONFIGURE THE PLUGIN TO GENERATE JAVA FILES
generateProtoTasks {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,15 @@
import org.vast.swe.SWEHelper;
import org.vast.swe.helper.GeoPosHelper;

import java.util.ArrayList;
import java.util.Base64;

/**
* Output specification and provider for {@link MeshtasticSensor}.
*/
public class MeshtasticOutputNodeInfo extends MeshtasticOutputPacketInfo implements MeshtasticOutputInterface{
static final String OUTPUT_NAME = "NodeInfo";
static final String OUTPUT_LABEL = "meshtastic Node Information Packet";
static final String OUTPUT_DESCRIPTION = "Output data for the Node Info";
private static final String OUTPUT_NAME = "NodeInfo";
private static final String OUTPUT_LABEL = "meshtastic Node Information Packet";
private static final String OUTPUT_DESCRIPTION = "Output data for the Node Info";

private final Object processingLock = new Object();

Expand Down Expand Up @@ -113,8 +112,6 @@ public void setData(MeshProtos.MeshPacket packetData, ByteString payload) {
String nodePrimaryKey = Base64.getEncoder().encodeToString(nodeInfoData.getPublicKey().toByteArray());
String nodeHwModel = nodeInfoData.getHwModel().toString();
String nodeRole = (nodeInfoData.getRole() == ConfigProtos.Config.DeviceConfig.Role.UNRECOGNIZED) ? "Unknown Role" : nodeInfoData.getRole().name();
// boolean isUnmessageable = node_info.getIsUnmessagable();
// boolean canBeMessaged = node_info.hasIsUnmessagable();

DataBlock dataBlock = latestRecord == null ? packetRecord.createDataBlock() : latestRecord.renew();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,9 @@
public class MeshtasticOutputPacketInfo extends VarRateSensorOutput<MeshtasticSensor> {
private static final double INITIAL_SAMPLING_PERIOD = 1.0;

public DataRecord packetRecord;
public DataEncoding dataEncoding;

public int packetRecordSize;
protected DataRecord packetRecord;
protected DataEncoding dataEncoding;
protected int packetRecordSize;

// Packet Variables
String packetId;
Expand Down Expand Up @@ -174,11 +173,11 @@ public void populatePacketDataStructure(DataBlock dataBlock){


public String convert32IntToString(int data){
long unsigned_num = Integer.toUnsignedLong(data);
return String.format("!%08x", unsigned_num);
long unsignedNum = Integer.toUnsignedLong(data);
return String.format("!%08x", unsignedNum);
}
public Instant convert32IntToInstant(int data){
long unsigned_num = Integer.toUnsignedLong(data);
return Instant.ofEpochSecond(unsigned_num);
long unsignedNum = Integer.toUnsignedLong(data);
return Instant.ofEpochSecond(unsignedNum);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
*/
public class MeshtasticOutputPosition extends MeshtasticOutputPacketInfo implements MeshtasticOutputInterface{
private static final String OUTPUT_NAME = "MeshtasticPosition";
private static final String OUTPUT_LABEL = "meshtastic Position Packet";
private static final String OUTPUT_DESCRIPTION = "Output data for a meshtastic Device's position";
private static final String OUTPUT_LABEL = "meshtastic Position Packet";
private static final String OUTPUT_DESCRIPTION = "Output data for a meshtastic Device's position";

private final Object processingLock = new Object();

Expand Down Expand Up @@ -58,7 +58,6 @@ void doInit() {
/**
* Sets the data for the output and publishes it.
*/
// public void setData(String packet_id, String packet_from, double lat, double lon, double alt) {
@Override
public void setData(MeshProtos.MeshPacket packetData, ByteString payload) {
synchronized (processingLock) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
* Output specification and provider for {@link MeshtasticSensor}.
*/
public class MeshtasticOutputTextMessage extends MeshtasticOutputPacketInfo implements MeshtasticOutputInterface{
static String OUTPUT_NAME = "MeshtasticText";
static String OUTPUT_LABEL = "meshtastic Text Message";
static String OUTPUT_DESCRIPTION = "Text provided by a meshtastic Device";
private static final String OUTPUT_NAME = "MeshtasticText";
private static final String OUTPUT_LABEL = "meshtastic Text Message";
private static final String OUTPUT_DESCRIPTION = "Text provided by a meshtastic Device";

private final Object processingLock = new Object();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
import org.sensorhub.api.comm.ICommProvider;
import org.sensorhub.api.common.SensorHubException;
import org.sensorhub.impl.sensor.AbstractSensorModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vast.ogc.om.MovingFeature;

import java.io.IOException;
Expand All @@ -36,7 +34,6 @@ public class MeshtasticSensor extends AbstractSensorModule<Config> {
static final String UID_PREFIX = "urn:osh:sensor:meshtastic:";
static final String XML_PREFIX = "meshtastic";

private static final Logger logger = LoggerFactory.getLogger(MeshtasticSensor.class);
private ICommProvider<?> commProvider;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
AtomicBoolean isProcessing = new AtomicBoolean(false);
Expand Down Expand Up @@ -185,61 +182,7 @@ private void startProcessing() {
try (InputStream in = commProvider.getInputStream()){
isProcessing.set(true);
while (isProcessing.get()) {
int b;
// find START1 in Input Stream, indicating that a Protobuf messages is being sent
do {
b = in.read();
if (b == -1) return;
// if (b != START1) {
//// optional: treat as debug ASCII
// System.out.print((char) b);
// }
} while (b != START1);

// If START1(0x94) is found, expect START2
b = in.read();
// invalid header
if (b != START2) {
getLogger().warn("Invalid header");
continue;
}

// GET LENGTH OF PROTOBUF MESSAGE
int lenMSB = in.read();
int lenLSB = in.read();
if (lenMSB == -1 || lenLSB == -1) break;

int length = ((lenMSB & 0xFF) << 8) | (lenLSB & 0xFF);
if (length <= 0 || length > 512) {
getLogger().info("Invalid length, resyncing...");
continue;
}

// PROTOBUF DATA
byte[] payload = new byte[length];
int read = 0;
while (read < length) {
int r = in.read(payload, read, length - read);
if (r == -1) break;
read += r;
}

if (read < length) {
getLogger().info("Truncated packet, resyncing...");
continue;
}

// parse protobuf
try {
MeshProtos.FromRadio msg = MeshProtos.FromRadio.parseFrom(payload);
if(msg.hasPacket()){
MeshProtos.MeshPacket packet = msg.getPacket();
meshtasticHandler.handlePacket(packet);
}
// getLogger().info("New message: " + msg);
} catch (Exception e) {
getLogger().error("Invalid protobuf: " + e.getMessage());
}
processNextPacket(in);
}

} catch (IOException e) {
Expand All @@ -252,6 +195,86 @@ private void startProcessing() {
});
}


private void processNextPacket(InputStream in) throws IOException {
// CHECK TO SEE IF PACKET SHOULD PROCESS
if (!findStartOfPacket(in)){
return;
}

// GET LENGTHH OF PACKET AND CHECK THAT IT MATCHES STYLE
int length = readPacketLength(in);
if (length <= 0 || length > 512) {
getLogger().info("Invalid length, resyncing...");
return;
}

// READ PROTOBUF PAYLOAD DATA
byte[] payload = readPayload(in, length);
if(payload.length == 0){
getLogger().info("Truncated packet, resyncing...");
return;
}

// PARSE PROTOBUF PAYLOAD USING MESHTASTIC PROTOS
parseProtobuf(payload);
}

private boolean findStartOfPacket(InputStream in) throws IOException {
int b;
// find START1 in Input Stream, indicating that a Protobuf messages is being sent
do {
b = in.read();
if (b == -1) return false;
} while (b != START1);

// If START1(0x94) is found, expect START2
b = in.read();
// invalid header
if (b != START2) {
getLogger().warn("Invalid header");
return false;
}

return true;
}

// GET LENGTH OF PROTOBUF MESSAGE
private int readPacketLength(InputStream in) throws IOException {
int lenMSB = in.read();
int lenLSB = in.read();
if (lenMSB == -1 || lenLSB == -1) return -1;
return ((lenMSB & 0xFF) <<8 | (lenLSB & 0xFF));
}

// READ A PAYLOAD FROM INPUT STREAM
private byte[] readPayload(InputStream in, int length) throws IOException {
byte[] payload = new byte[length];
int read = 0;
while(read < length ){
int r = in.read(payload,read,length-read);
if (r == -1) {
return new byte[0];
}
read += r;
}
return payload;
}

// PARSE MESHTASTIC RADIO MESSAGE
private void parseProtobuf(byte[] payload){
try {
MeshProtos.FromRadio msg = MeshProtos.FromRadio.parseFrom(payload);
if(msg.hasPacket()){
MeshProtos.MeshPacket packet = msg.getPacket();
meshtasticHandler.handlePacket(packet);
}
} catch (Exception e) {
getLogger().error("Invalid protobuf: {} ", e.getMessage());
}
}


public boolean sendMessage(MeshProtos.ToRadio message) {
byte[] bytes = message.toByteArray();

Expand Down
Loading