/* https://www.decentlab.com/products/tipping-bucket-rain-gauge-for-lorawan */ using System; using System.IO; using System.Collections.Generic; public class DecentlabDecoder { private delegate double conversion(double[] x); public const int PROTOCOL_VERSION = 2; /* device-specific parameters */ public const double resolution = 0.1; private class Sensor { internal int length { get; set; } internal List values { get; set; } internal Sensor(int length, List values) { this.length = length; this.values = values; } } private class SensorValue { internal string name { get; set; } internal string unit { get; set; } internal conversion convert; internal SensorValue(string name, string unit, conversion convert) { this.name = name; this.unit = unit; this.convert = convert; } } private static readonly List SENSORS = new List() { new Sensor(4, new List() { new SensorValue("Precipitation", "mm", x => x[0] * resolution), new SensorValue("Precipitation interval", "s", x => x[1]), new SensorValue("Cumulative precipitation", "mm", x => (x[2] + x[3] * 65536) * resolution) }), new Sensor(1, new List() { new SensorValue("Battery voltage", "V", x => x[0] / 1000) }) }; private static int ReadInt(Stream stream) { return (stream.ReadByte() << 8) + stream.ReadByte(); } public static Dictionary> Decode(byte[] msg) { return Decode(new MemoryStream(msg)); } public static Dictionary> Decode(String msg) { byte[] output = new byte[msg.Length / 2]; for (int i = 0, j = 0; i < msg.Length; i += 2, j++) { output[j] = (byte)int.Parse(msg.Substring(i, 2), System.Globalization.NumberStyles.HexNumber); } return Decode(output); } public static Dictionary> Decode(Stream msg) { var version = msg.ReadByte(); if (version != PROTOCOL_VERSION) { throw new InvalidDataException("protocol version " + version + " doesn't match v2"); } var result = new Dictionary>(); result["Protocol version"] = new Tuple(version, null); var deviceId = ReadInt(msg); result["Device ID"] = new Tuple(deviceId, null); var flags = ReadInt(msg); foreach (var sensor in SENSORS) { if ((flags & 1) == 1) { double[] x = new double[sensor.length]; for (int i = 0; i < sensor.length; i++) { x[i] = ReadInt(msg); } foreach (var val in sensor.values) { if (val.convert != null) { result[val.name] = new Tuple(val.convert(x), val.unit); } } } flags >>= 1; } return result; } } public class Program { public static void Main() { var payloads = new string[] { "0202f8000300040258409a00000c54", "0202f800020c54" }; foreach (var pl in payloads) { var decoded = DecentlabDecoder.Decode(pl); foreach (var k in decoded.Keys) { Console.WriteLine(k + ": " + decoded[k]); } Console.WriteLine(); } } }