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