Added telemetry to repeater interface.

This commit is contained in:
Winston Lowe
2026-01-07 00:49:35 -08:00
parent e3d7607db9
commit c306ad798c
6 changed files with 670 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
import 'dart:convert';
import 'dart:typed_data';
class BufferReader {
int pointer = 0;
final Uint8List buffer;
BufferReader(Uint8List data) : buffer = Uint8List.fromList(data);
int getRemainingBytesCount() {
return buffer.length - pointer;
}
int readByte() {
return readBytes(1)[0];
}
Uint8List readBytes(int count) {
final data = buffer.sublist(pointer, pointer + count);
pointer += count;
return data;
}
Uint8List readRemainingBytes() {
return readBytes(getRemainingBytesCount());
}
String readString() {
return utf8.decode(readRemainingBytes());
}
String readCString(int maxLength) {
final value = <int>[];
final bytes = readBytes(maxLength);
for (final byte in bytes) {
// if we find a null terminator character, we have reached the end of the cstring
if (byte == 0) {
return utf8.decode(Uint8List.fromList(value));
}
value.add(byte);
}
return utf8.decode(Uint8List.fromList(value));
}
int readInt8() {
final bytes = readBytes(1);
return ByteData.view(bytes.buffer).getInt8(0);
}
int readUInt8() {
final bytes = readBytes(1);
return ByteData.view(bytes.buffer).getUint8(0);
}
int readUInt16LE() {
final bytes = readBytes(2);
return ByteData.view(bytes.buffer).getUint16(0, Endian.little);
}
int readUInt16BE() {
final bytes = readBytes(2);
return ByteData.view(bytes.buffer).getUint16(0, Endian.big);
}
int readUInt32LE() {
final bytes = readBytes(4);
return ByteData.view(bytes.buffer).getUint32(0, Endian.little);
}
int readUInt32BE() {
final bytes = readBytes(4);
return ByteData.view(bytes.buffer).getUint32(0, Endian.big);
}
int readInt16LE() {
final bytes = readBytes(2);
return ByteData.view(bytes.buffer).getInt16(0, Endian.little);
}
int readInt16BE() {
final bytes = readBytes(2);
return ByteData.view(bytes.buffer).getInt16(0, Endian.big);
}
int readInt32LE() {
final bytes = readBytes(4);
return ByteData.view(bytes.buffer).getInt32(0, Endian.little);
}
int readInt24BE() {
// read 24-bit (3 bytes) big endian integer
var value = (readByte() << 16) | (readByte() << 8) | readByte();
// convert 24-bit signed integer to 32-bit signed integer
// 0x800000 is the sign bit for a 24-bit value
// if it's set, value is negative in 24-bit two's complement
if ((value & 0x800000) != 0) {
value -= 0x1000000;
}
return value;
}
}
+57
View File
@@ -0,0 +1,57 @@
import 'dart:convert';
import 'dart:typed_data';
class BufferWriter {
final BytesBuilder _builder = BytesBuilder();
Uint8List toBytes() {
return _builder.toBytes();
}
void writeBytes(Uint8List bytes) {
_builder.add(bytes);
}
void writeByte(int byte) {
_builder.addByte(byte);
}
void writeUInt16LE(int num) {
final bytes = Uint8List(2);
final data = ByteData.view(bytes.buffer);
data.setUint16(0, num, Endian.little);
writeBytes(bytes);
}
void writeUInt32LE(int num) {
final bytes = Uint8List(4);
final data = ByteData.view(bytes.buffer);
data.setUint32(0, num, Endian.little);
writeBytes(bytes);
}
void writeInt32LE(int num) {
final bytes = Uint8List(4);
final data = ByteData.view(bytes.buffer);
data.setInt32(0, num, Endian.little);
writeBytes(bytes);
}
void writeString(String string) {
writeBytes(Uint8List.fromList(utf8.encode(string)));
}
void writeCString(String string, int maxLength) {
final bytes = Uint8List(maxLength);
final encodedString = utf8.encode(string);
for (var i = 0; i < maxLength && i < encodedString.length; i++) {
bytes[i] = encodedString[i];
}
// ensure the last byte is always a null terminator
bytes[maxLength - 1] = 0;
writeBytes(bytes);
}
}
+191
View File
@@ -0,0 +1,191 @@
import 'dart:typed_data';
import 'buffer_reader.dart';
import 'buffer_writer.dart';
class CayenneLpp {
static const int lppDigitalInput = 0; // 1 byte
static const int lppDigitalOutput = 1; // 1 byte
static const int lppAnalogInput = 2; // 2 bytes, 0.01 signed
static const int lppAnalogOutput = 3; // 2 bytes, 0.01 signed
static const int lppGenericSensor = 100; // 4 bytes, unsigned
static const int lppLuminosity = 101; // 2 bytes, 1 lux unsigned
static const int lppPresence = 102; // 1 byte, bool
static const int lppTemperature = 103; // 2 bytes, 0.1°C signed
static const int lppRelativeHumidity = 104; // 1 byte, 0.5% unsigned
static const int lppAccelerometer = 113; // 2 bytes per axis, 0.001G
static const int lppBarometricPressure = 115; // 2 bytes 0.1hPa unsigned
static const int lppVoltage = 116; // 2 bytes 0.01V unsigned
static const int lppCurrent = 117; // 2 bytes 0.001A unsigned
static const int lppFrequency = 118; // 4 bytes 1Hz unsigned
static const int lppPercentage = 120; // 1 byte 1-100% unsigned
static const int lppAltitude = 121; // 2 byte 1m signed
static const int lppConcentration = 125; // 2 bytes, 1 ppm unsigned
static const int lppPower = 128; // 2 byte, 1W, unsigned
static const int lppDistance = 130; // 4 byte, 0.001m, unsigned
static const int lppEnergy = 131; // 4 byte, 0.001kWh, unsigned
static const int lppDirection = 132; // 2 bytes, 1deg, unsigned
static const int lppUnixTime = 133; // 4 bytes, unsigned
static const int lppGyrometer = 134; // 2 bytes per axis, 0.01 °/s
static const int lppColour = 135; // 1 byte per RGB Color
static const int lppGps = 136; // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01 meter
static const int lppSwitch = 142; // 1 byte, 0/1
static const int lppPolyline = 240; // 1 byte size, 1 byte delta factor, 3 byte lon/lat 0.0001° * factor, n (size-8) bytes deltas
final BufferWriter _writer = BufferWriter();
Uint8List toBytes() {
return _writer.toBytes();
}
void addDigitalInput(int channel, int value) {
_writer.writeByte(channel);
_writer.writeByte(lppDigitalInput);
_writer.writeByte(value);
}
void addTemperature(int channel, double value) {
_writer.writeByte(channel);
_writer.writeByte(lppTemperature);
final val = (value * 10).toInt();
_writer.writeBytes(_int16ToBE(val));
}
void addVoltage(int channel, double value) {
_writer.writeByte(channel);
_writer.writeByte(lppVoltage);
final val = (value * 100).toInt();
_writer.writeBytes(_int16ToBE(val));
}
void addGps(int channel, double lat, double lon, double alt) {
_writer.writeByte(channel);
_writer.writeByte(lppGps);
_writer.writeBytes(_int24ToBE((lat * 10000).toInt()));
_writer.writeBytes(_int24ToBE((lon * 10000).toInt()));
_writer.writeBytes(_int24ToBE((alt * 100).toInt()));
}
Uint8List _int16ToBE(int value) {
final bytes = Uint8List(2);
final data = ByteData.view(bytes.buffer);
data.setInt16(0, value, Endian.big);
return bytes;
}
Uint8List _int24ToBE(int value) {
final bytes = Uint8List(3);
bytes[0] = (value >> 16) & 0xFF;
bytes[1] = (value >> 8) & 0xFF;
bytes[2] = value & 0xFF;
return bytes;
}
static List<Map<String, dynamic>> parse(Uint8List bytes) {
final buffer = BufferReader(bytes);
final telemetry = <Map<String, dynamic>>[];
while (buffer.getRemainingBytesCount() >= 2) {
final channel = buffer.readUInt8();
final type = buffer.readUInt8();
if (channel == 0 && type == 0) {
break;
}
switch (type) {
case lppGenericSensor:
telemetry.add({
'channel': channel,
'type': type,
'value': buffer.readUInt32BE(),
});
break;
case lppLuminosity:
telemetry.add({
'channel': channel,
'type': type,
'value': buffer.readUInt16BE(),
});
break;
case lppPresence:
telemetry.add({
'channel': channel,
'type': type,
'value': buffer.readUInt8(),
});
break;
case lppTemperature:
telemetry.add({
'channel': channel,
'type': type,
'value': buffer.readInt16BE() / 10,
});
break;
case lppRelativeHumidity:
telemetry.add({
'channel': channel,
'type': type,
'value': buffer.readUInt8() / 2,
});
break;
case lppBarometricPressure:
telemetry.add({
'channel': channel,
'type': type,
'value': buffer.readUInt16BE() / 10,
});
break;
case lppVoltage:
telemetry.add({
'channel': channel,
'type': type,
'value': buffer.readInt16BE() / 100,
});
break;
case lppCurrent:
telemetry.add({
'channel': channel,
'type': type,
'value': buffer.readInt16BE() / 1000,
});
break;
case lppPercentage:
telemetry.add({
'channel': channel,
'type': type,
'value': buffer.readUInt8(),
});
break;
case lppConcentration:
telemetry.add({
'channel': channel,
'type': type,
'value': buffer.readUInt16BE(),
});
break;
case lppPower:
telemetry.add({
'channel': channel,
'type': type,
'value': buffer.readUInt16BE(),
});
break;
case lppGps:
telemetry.add({
'channel': channel,
'type': type,
'value': {
'latitude': buffer.readInt24BE() / 10000,
'longitude': buffer.readInt24BE() / 10000,
'altitude': buffer.readInt24BE() / 100,
},
});
break;
default:
return telemetry;
}
}
return telemetry;
}
}