After some testing with more client devices, especially the Easy Sensors Arduino Nano radio shield RFM69/95 or NRF24L01+ I have decided to move to non text addresses for devices and the LoRa field gateway.
THIS IS A BREAKING CHANGE
The unique identifier provided by the SHA204A crypto and authentication chip on the EasySensors shield highlighted this issue. The Binary Coded Decimal(BCD) version of the 72 bit identifier was too long to fit in the from address.
My Arduino MKR1300 sample code has some helper functions to populate the message header, add values, and prepare the message payload for reuse.
On the server side I have added code to log the build version and Raspbery PI shield type
// Log the Application build, shield information etc. LoggingFields appllicationBuildInformation = new LoggingFields(); #if DRAGINO appllicationBuildInformation.AddString("Shield", "DraginoLoRaGPSHat"); #endif ... #if UPUTRONICS_RPIPLUS_CS1 appllicationBuildInformation.AddString("Shield", "UputronicsPiPlusLoRaExpansionBoardCS1"); #endif appllicationBuildInformation.AddString("Timezone", TimeZoneSettings.CurrentTimeZoneDisplayName); appllicationBuildInformation.AddString("OSVersion", Environment.OSVersion.VersionString); appllicationBuildInformation.AddString("MachineName", Environment.MachineName); // This is from the application manifest Package package = Package.Current; PackageId packageId = package.Id; PackageVersion version = packageId.Version; appllicationBuildInformation.AddString("ApplicationVersion", string.Format($"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}")); this.logging.LogEvent("Application starting", appllicationBuildInformation, LoggingLevel.Information);
Then when the message payload is populated the from address byte array is converted to BCD
private async void Rfm9XDevice_OnReceive(object sender, Rfm9XDevice.OnDataReceivedEventArgs e) { string addressBcdText; string messageBcdText; string messageText = ""; char[] sensorReadingSeparators = new char[] { ',' }; char[] sensorIdAndValueSeparators = new char[] { ' ' }; addressBcdText = BitConverter.ToString(e.Address); messageBcdText = BitConverter.ToString(e.Data); try { messageText = UTF8Encoding.UTF8.GetString(e.Data); } catch (Exception) { this.logging.LogMessage("Failure converting payload to text", LoggingLevel.Error); return; } #if DEBUG Debug.WriteLine(@"{0:HH:mm:ss}-RX From {1} PacketSnr {2:0.0} Packet RSSI {3}dBm RSSI {4}dBm = {5} byte message ""{6}""", DateTime.Now, messageBcdText, e.PacketSnr, e.PacketRssi, e.Rssi, e.Data.Length, messageText); #endif LoggingFields messagePayload = new LoggingFields(); messagePayload.AddInt32("AddressLength", e.Address.Length); messagePayload.AddString("Address-BCD", addressBcdText); messagePayload.AddInt32("Message-Length", e.Data.Length); messagePayload.AddString("Message-BCD", messageBcdText); messagePayload.AddString("Message-Unicode", messageText); messagePayload.AddDouble("Packet SNR", e.PacketSnr); messagePayload.AddInt32("Packet RSSI", e.PacketRssi); messagePayload.AddInt32("RSSI", e.Rssi); this.logging.LogEvent("Message Data", messagePayload, LoggingLevel.Verbose); //... JObject telemetryDataPoint = new JObject(); // This could be simplified but for field gateway will use this style LoggingFields sensorData = new LoggingFields(); telemetryDataPoint.Add("DeviceID", addressBcdText); sensorData.AddString("DeviceID", addressBcdText); telemetryDataPoint.Add("PacketSNR", e.PacketSnr.ToString("F1")); sensorData.AddString("PacketSNR", e.PacketSnr.ToString("F1")); telemetryDataPoint.Add("PacketRSSI", e.PacketRssi); sensorData.AddInt32("PacketRSSI", e.PacketRssi); telemetryDataPoint.Add("RSSI", e.Rssi); sensorData.AddInt32("RSSI", e.Rssi); //Chop up each sensor read into an ID & value foreach (string sensorReading in sensorReadings) { string[] sensorIdAndValue = sensorReading.Split(sensorIdAndValueSeparators, StringSplitOptions.RemoveEmptyEntries); // Check that there is an id & value if (sensorIdAndValue.Length != 2) { this.logging.LogMessage("Sensor reading invalid format", LoggingLevel.Warning); return; } string sensorId = sensorIdAndValue[0]; string value = sensorIdAndValue[1]; try { if (this.applicationSettings.SensorIDIsDeviceIDSensorID) { // Construct the sensor ID from SensordeviceID & Value ID telemetryDataPoint.Add(string.Format("{0}{1}", addressBcdText, sensorId), value); sensorData.AddString(string.Format("{0}{1}", addressBcdText, sensorId), value); Debug.WriteLine(" Sensor {0}{1} Value {2}", addressBcdText, sensorId, value); } else { telemetryDataPoint.Add(sensorId, value); sensorData.AddString(sensorId, value); Debug.WriteLine(" Device {0} Sensor {1} Value {2}", addressBcdText, sensorId, value); } } catch (Exception ex) { this.logging.LogMessage("Sensor reading invalid JSON format " + ex.Message, LoggingLevel.Warning); return; } } this.logging.LogEvent("Sensor readings", sensorData, LoggingLevel.Information); try { using (Message message = new Message(Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(telemetryDataPoint)))) { Debug.WriteLine(" AzureIoTHubClient SendEventAsync start"); await this.azureIoTHubClient.SendEventAsync(message); Debug.WriteLine(" AzureIoTHubClient SendEventAsync finish"); } } catch (Exception ex) { this.logging.LogMessage("AzureIoTHubClient SendEventAsync failed " + ex.Message, LoggingLevel.Error); } }

This does mean longer field names but I usually copy n paste them from the Arduino serial monitor of the Event Tracing For Windows (ETW) logging.

Pingback: Grove – Carbon Dioxide Sensor(SCD30) trial | devMobile's blog
Pingback: Grove – Laser PM2.5 Sensor(HM3301) trial | devMobile's blog
Pingback: DF Robot Temperature & Humidity Sensor(SHT20) trial | devMobile's blog
Pingback: Grove-VOC and eCO2 Gas Sensor (SGP30) | devMobile's blog