From f6b63169f546c33c2813d72026610dde1ac62006 Mon Sep 17 00:00:00 2001 From: dernasherbrezon Date: Thu, 28 Dec 2023 21:41:30 +0000 Subject: [PATCH] add support for ble protocol + upgrade to version 2 --- .../ru/r2cloud/device/LoraAtBleDevice.java | 14 ++-- .../lora/loraat/gatt/DeviceStatus.java | 53 ------------- .../r2cloud/lora/loraat/gatt/GattServer.java | 2 +- .../lora/loraat/gatt/LoraAtDeviceStatus.java | 60 ++++++++++++++ .../loraat/gatt/ScheduleCharacteristic.java | 9 ++- .../loraat/gatt/StatusCharacteristic.java | 78 +++++++++++++++++++ .../satellite/reader/LoraAtBleReader.java | 2 +- 7 files changed, 154 insertions(+), 64 deletions(-) delete mode 100644 src/main/java/ru/r2cloud/lora/loraat/gatt/DeviceStatus.java create mode 100644 src/main/java/ru/r2cloud/lora/loraat/gatt/LoraAtDeviceStatus.java create mode 100644 src/main/java/ru/r2cloud/lora/loraat/gatt/StatusCharacteristic.java diff --git a/src/main/java/ru/r2cloud/device/LoraAtBleDevice.java b/src/main/java/ru/r2cloud/device/LoraAtBleDevice.java index 63b02d59..c9ce39e2 100644 --- a/src/main/java/ru/r2cloud/device/LoraAtBleDevice.java +++ b/src/main/java/ru/r2cloud/device/LoraAtBleDevice.java @@ -7,6 +7,7 @@ import org.slf4j.LoggerFactory; import ru.r2cloud.lora.LoraFrame; +import ru.r2cloud.lora.loraat.gatt.LoraAtDeviceStatus; import ru.r2cloud.model.DeviceConfiguration; import ru.r2cloud.model.DeviceConnectionStatus; import ru.r2cloud.model.DeviceStatus; @@ -59,14 +60,11 @@ public DeviceStatus getStatus() { return result; } - public void setStatus(int batteryLevel, int signalLevel) { - LOG.info("[{}] battery level: {} signal: {}", id, batteryLevel, signalLevel); - if (batteryLevel == 255) { - this.batteryLevel = null; - } else { - this.batteryLevel = batteryLevel; - } - this.signalLevel = signalLevel; + //TODO update rrd graphs + public void updateStatus(LoraAtDeviceStatus status) { + LOG.info("[{}] signal: {}", id, status.getBluetoothRssi()); + this.batteryLevel = null; + this.signalLevel = status.getBluetoothRssi(); } public void addFrame(LoraFrame frame) { diff --git a/src/main/java/ru/r2cloud/lora/loraat/gatt/DeviceStatus.java b/src/main/java/ru/r2cloud/lora/loraat/gatt/DeviceStatus.java deleted file mode 100644 index f87b0a16..00000000 --- a/src/main/java/ru/r2cloud/lora/loraat/gatt/DeviceStatus.java +++ /dev/null @@ -1,53 +0,0 @@ -package ru.r2cloud.lora.loraat.gatt; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import ru.r2cloud.device.Device; -import ru.r2cloud.device.DeviceManager; -import ru.r2cloud.device.LoraAtBleDevice; -import ru.r2cloud.util.Configuration; - -public class DeviceStatus extends BleCharacteristic { - - private static final Logger LOG = LoggerFactory.getLogger(DeviceStatus.class); - private final DeviceManager manager; - - public DeviceStatus(String objectPath, String[] flags, String uuId, String servicePath, BleDescriptor descriptor, DeviceManager manager) { - super(objectPath, flags, uuId, servicePath, descriptor); - this.manager = manager; - } - - @Override - public byte[] read(String bluetoothAddress) { - // unsupported - return new byte[0]; - } - - @Override - public void write(byte[] value, String bluetoothAddress) { - if (value.length < 2) { - LOG.info("[{}] not enough bytes. expected 2, got: {}", bluetoothAddress, value.length); - return; - } - LoraAtBleDevice device = getLoraDevice(bluetoothAddress); - if (device == null) { - return; - } - device.setStatus(value[0] & 0xFF, (int) value[1]); - } - - private LoraAtBleDevice getLoraDevice(String bluetoothAddress) { - Device device = manager.findDeviceById(Configuration.LORA_AT_DEVICE_PREFIX + bluetoothAddress); - if (device == null) { - LOG.info("[{}] ble device is not configured", bluetoothAddress); - return null; - } - if (!(device instanceof LoraAtBleDevice)) { - LOG.info("not a lora-at device: {}", bluetoothAddress); - return null; - } - return (LoraAtBleDevice) device; - } - -} diff --git a/src/main/java/ru/r2cloud/lora/loraat/gatt/GattServer.java b/src/main/java/ru/r2cloud/lora/loraat/gatt/GattServer.java index 24e70c23..dbe0c9cb 100644 --- a/src/main/java/ru/r2cloud/lora/loraat/gatt/GattServer.java +++ b/src/main/java/ru/r2cloud/lora/loraat/gatt/GattServer.java @@ -77,7 +77,7 @@ public void start() { ScheduleCharacteristic schedule = new ScheduleCharacteristic(LORA_SCHEDULE_PATH, new String[] { "read", "write" }, SCHEDULE_CHARACTERISTIC_UUID, LORA_SERVICE_PATH, scheduleDesc, manager, clock); BleDescriptor statusDesc = new BleDescriptor(LORA_STATUS_PATH + "/desc0", new String[] { "read" }, "5604f205-0c14-4926-9d7d-21dbab315f2f", LORA_STATUS_PATH, "Status of all connected LoRa modules"); - DeviceStatus status = new DeviceStatus(LORA_STATUS_PATH, new String[] { "write" }, STATUS_CHARACTERISTIC_UUID, LORA_SERVICE_PATH, statusDesc, manager); + StatusCharacteristic status = new StatusCharacteristic(LORA_STATUS_PATH, new String[] { "write" }, STATUS_CHARACTERISTIC_UUID, LORA_SERVICE_PATH, statusDesc, manager); List characteristics = new ArrayList<>(); characteristics.add(schedule); characteristics.add(status); diff --git a/src/main/java/ru/r2cloud/lora/loraat/gatt/LoraAtDeviceStatus.java b/src/main/java/ru/r2cloud/lora/loraat/gatt/LoraAtDeviceStatus.java new file mode 100644 index 00000000..ad5d89a6 --- /dev/null +++ b/src/main/java/ru/r2cloud/lora/loraat/gatt/LoraAtDeviceStatus.java @@ -0,0 +1,60 @@ +package ru.r2cloud.lora.loraat.gatt; + +public class LoraAtDeviceStatus { + + private int bluetoothRssi; + private int sx127xRawTemperature; + private int solarVoltage; + private int solarCurrent; + private int batteryVoltage; + private int batteryCurrent; + + public int getBluetoothRssi() { + return bluetoothRssi; + } + + public void setBluetoothRssi(int bluetoothRssi) { + this.bluetoothRssi = bluetoothRssi; + } + + public int getSx127xRawTemperature() { + return sx127xRawTemperature; + } + + public void setSx127xRawTemperature(int sx127xRawTemperature) { + this.sx127xRawTemperature = sx127xRawTemperature; + } + + public int getSolarVoltage() { + return solarVoltage; + } + + public void setSolarVoltage(int solarVoltage) { + this.solarVoltage = solarVoltage; + } + + public int getSolarCurrent() { + return solarCurrent; + } + + public void setSolarCurrent(int solarCurrent) { + this.solarCurrent = solarCurrent; + } + + public int getBatteryVoltage() { + return batteryVoltage; + } + + public void setBatteryVoltage(int batteryVoltage) { + this.batteryVoltage = batteryVoltage; + } + + public int getBatteryCurrent() { + return batteryCurrent; + } + + public void setBatteryCurrent(int batteryCurrent) { + this.batteryCurrent = batteryCurrent; + } + +} diff --git a/src/main/java/ru/r2cloud/lora/loraat/gatt/ScheduleCharacteristic.java b/src/main/java/ru/r2cloud/lora/loraat/gatt/ScheduleCharacteristic.java index 7bbb06d8..7aa00eec 100644 --- a/src/main/java/ru/r2cloud/lora/loraat/gatt/ScheduleCharacteristic.java +++ b/src/main/java/ru/r2cloud/lora/loraat/gatt/ScheduleCharacteristic.java @@ -25,6 +25,7 @@ public class ScheduleCharacteristic extends BleCharacteristic { private static final Logger LOG = LoggerFactory.getLogger(ScheduleCharacteristic.class); + private static final int PROTOCOL_VERSION = 2; private final DeviceManager manager; private final Clock clock; @@ -66,6 +67,7 @@ public byte[] read(String bluetoothAddress) { } ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (DataOutputStream dos = new DataOutputStream(baos)) { + dos.writeByte(PROTOCOL_VERSION); dos.writeLong(req.getStartTimeMillis()); dos.writeLong(req.getEndTimeMillis()); dos.writeLong(currentTime); @@ -91,7 +93,7 @@ public byte[] read(String bluetoothAddress) { // lora packet size cannot be more than 255 bytes dos.writeByte(transmitter.getBeaconSizeBytes()); dos.writeShort(240); // over current protection. not used for RX - dos.writeByte(0); // pin for TX. not used in RX + dos.writeByte(0); // pin for TX. not used in RX } catch (IOException e) { LOG.error("[{}] can't serialize output", bluetoothAddress, e); return new byte[0]; @@ -108,6 +110,11 @@ public void write(byte[] value, String bluetoothAddress) { ByteArrayInputStream bais = new ByteArrayInputStream(value); try (DataInputStream dis = new DataInputStream(bais)) { LoraFrame frame = new LoraFrame(); + int protocolVersion = dis.readUnsignedByte(); + if (protocolVersion != PROTOCOL_VERSION) { + LOG.error("[{}] invalid protocol version {}, expected {}", bluetoothAddress, protocolVersion, PROTOCOL_VERSION); + return; + } frame.setFrequencyError(dis.readInt()); frame.setRssi(dis.readShort()); frame.setSnr(dis.readFloat()); diff --git a/src/main/java/ru/r2cloud/lora/loraat/gatt/StatusCharacteristic.java b/src/main/java/ru/r2cloud/lora/loraat/gatt/StatusCharacteristic.java new file mode 100644 index 00000000..daf04897 --- /dev/null +++ b/src/main/java/ru/r2cloud/lora/loraat/gatt/StatusCharacteristic.java @@ -0,0 +1,78 @@ +package ru.r2cloud.lora.loraat.gatt; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ru.r2cloud.device.Device; +import ru.r2cloud.device.DeviceManager; +import ru.r2cloud.device.LoraAtBleDevice; +import ru.r2cloud.util.Configuration; +import ru.r2cloud.util.Util; + +public class StatusCharacteristic extends BleCharacteristic { + + private static final Logger LOG = LoggerFactory.getLogger(StatusCharacteristic.class); + private static final int PROTOCOL_VERSION = 2; + private final DeviceManager manager; + + public StatusCharacteristic(String objectPath, String[] flags, String uuId, String servicePath, BleDescriptor descriptor, DeviceManager manager) { + super(objectPath, flags, uuId, servicePath, descriptor); + this.manager = manager; + } + + @Override + public byte[] read(String bluetoothAddress) { + // unsupported + return new byte[0]; + } + + @Override + public void write(byte[] value, String bluetoothAddress) { + if (value.length < 2) { + LOG.info("[{}] not enough bytes. expected 2, got: {}", bluetoothAddress, value.length); + return; + } + LoraAtBleDevice device = getLoraDevice(bluetoothAddress); + if (device == null) { + return; + } + ByteArrayInputStream bais = new ByteArrayInputStream(value); + try (DataInputStream dis = new DataInputStream(bais)) { + int protocolVersion = dis.readUnsignedByte(); + if (protocolVersion != PROTOCOL_VERSION) { + LOG.error("[{}] invalid protocol version {}, expected {}", bluetoothAddress, protocolVersion, PROTOCOL_VERSION); + return; + } + LoraAtDeviceStatus status = new LoraAtDeviceStatus(); + status.setBluetoothRssi(dis.readByte()); + status.setSx127xRawTemperature(dis.readByte()); + status.setSolarVoltage(dis.readUnsignedShort()); + status.setSolarCurrent(dis.readShort()); + status.setBatteryVoltage(dis.readUnsignedShort()); + status.setBatteryCurrent(dis.readShort()); + device.updateStatus(status); + } catch (IOException e) { + Util.logIOException(LOG, false, "[" + bluetoothAddress + "] can't read input", e); + return; + } + + } + + private LoraAtBleDevice getLoraDevice(String bluetoothAddress) { + Device device = manager.findDeviceById(Configuration.LORA_AT_DEVICE_PREFIX + bluetoothAddress); + if (device == null) { + LOG.info("[{}] ble device is not configured", bluetoothAddress); + return null; + } + if (!(device instanceof LoraAtBleDevice)) { + LOG.info("not a lora-at device: {}", bluetoothAddress); + return null; + } + return (LoraAtBleDevice) device; + } + +} diff --git a/src/main/java/ru/r2cloud/satellite/reader/LoraAtBleReader.java b/src/main/java/ru/r2cloud/satellite/reader/LoraAtBleReader.java index 3550a49c..3616adc1 100644 --- a/src/main/java/ru/r2cloud/satellite/reader/LoraAtBleReader.java +++ b/src/main/java/ru/r2cloud/satellite/reader/LoraAtBleReader.java @@ -76,7 +76,7 @@ public void complete() { } private String toLoraAtFriendlyString() { - return String.format("%f,%f,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", transmitter.getFrequency() / 1000000.0f, transmitter.getLoraBandwidth() / 1000.0f, transmitter.getLoraSpreadFactor(), transmitter.getLoraCodingRate(), transmitter.getLoraSyncword(), 10, transmitter.getLoraPreambleLength(), + return String.format("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", transmitter.getFrequency(), transmitter.getLoraBandwidth(), transmitter.getLoraSpreadFactor(), transmitter.getLoraCodingRate(), transmitter.getLoraSyncword(), transmitter.getLoraPreambleLength(), (int) deviceConfiguration.getGain(), transmitter.getLoraLdro(), transmitter.isLoraCrc() ? 1 : 0, transmitter.isLoraExplicitHeader() ? 1 : 0, transmitter.getBeaconSizeBytes()); }