I teach kids how to code and needed a telemetry field gateway which was reasonably priced (both devices + field gateway hardware), which would work across a campus (couple of city blocks), had low power consumption (solar powered devices) and had good in building penetration (selection of indoor & outdoor sensors).
EDIT 2018-11-24 based on feedback from users prebuilt installers are now available from GitHub. There are eight different configurations to build and test so there maybe some delays before updated installers for your hardware are available.
All you have to do is download latest installer file for your shield locally then open the "Device portal" on your device
1. Navigate to Apps\Apps Manager "Add" to install
2. Then "Start" and "Stop" the application which will create config file template
3. Navigate to Apps\File Explorer to download config file from \User Folders\LocalAppData\Azure.IoTHub.IoTCore.FieldGateway.LoRa...\LocalState\
4. Modify config file putting in your AzureIoTHub connection string, and changing frequency etc. as required
5. The use File Explorer to upload config file
6. Navigate to Apps\Apps Manager to start application and/or set application to "Startup" automatically
I also needed something for IoT Meetup demos where I put sensors outside the venue and pass other into to the audience so they can see indoor & outdoor data being streamed and changing in real-time.
I have nRF24L01 Field Gateways for Azure IoT Hubs & AdaFruit.IO so a LoRa field gateway looked like a good option.
Initially, I am using AMQP, but getting an HTTP endpoint (fine for telemetry) white-listed in a school environment is easier so the configuration also supports HTTP (Should do MQTT as well)
I also had some issues with the current (Nov 2018) version of the Microsoft.Azure.Devices.Client NuGet package so upgrade with caution.
The application generates a sample configuration file if one is not found. Start the program, download the configuration file add your Azure IoT Hub connection string and change any other settings then upload.
Currently the codebase supports (tested with) these Raspberry PI shields
- Dragino LoRa GPS Hat for Raspberry Pi
- Elecrow LoRa RFM95 IoT Board for RPI
- M2M 1 Channel LoRaWan Gateway Shield for Raspberry PI
- Lora/LoraWan Shield for Raspberry PI Zero and PI 3 (needs a spacer on PI3)
- Uputronics Raspberry PI Zero LoRa Expansion Board
- Uputronics Raspberry Pi+ LoRa Expansion Board
- Adafruit LoRa Radio Bonnet With OLED @915MHZ (Feb 2019)
- Adafruit LoRa Radio Bonnet With OLED @433MHZ (Feb 2019)
The core field gateway code is roughly 550 Lines long and uses my .Net RFM9X library (with payload addressing enabled). The packets have a simple header which has to & from addresses and the payload. For ease of student development/debugging the payload is comma delimited ASCII, rather than a packed binary protocol.(A future enhancement)
I have roughly a dozen sample clients (working on a couple more LowPower Lab clients) on my blog and I will publish them on Hackster.IO over the next couple of weeks.
- Arduino using Dragino, elecrow and MakerFabs shields
- Netduino using Dragino, elecrow and MakerFabs shields
- Maduino
- IoTNet
- IoTMCU915
- AdaFruit Feather M0
- Elecrow 32u4 with LoRa RFM95 IoT Boatd
- M2M Low power LoRaWan Node Model A328
- M2M Low power LoRaWan Node Model B1284
- Wisen Whisper Node LoRa
- Dragino LoRa Mini Dev
- Arduino Nano with EasySensors Arduino Nano shield RFM95
- Arduino MKR WAN 1300
- Moteino M0
The application logs diagnostic information to the ETW logging
Once the application is configured and tested you can use the Device Portal\Apps Manager to set the program to run on device startup.
Currently (Nov 2018) the application publishes a number of Azure IoT properties which are useful for provisioning and remote device management.
The application can upload data to Azure IoT Central, the key setting is SensorIDIsDeviceIDSensorID which has to be set to True.
The application supports the following configuration settings. The minimal configuration file is
{
"AzureIoTHubDeviceConnectionString": "HostName=saas-iothub-12345678-9012-3456-7890-123456789012.azure-devices.net;DeviceId=b1234567890d;SharedAccessKey=qwertyuiopasdfghjklzxcvbnm1234567890qwertyu=",
"AzureIoTHubTransportType": "amqp",
"Address": "LoRaIoT1",
"Frequency": 915000000.0
}
The software has been tested with 433MHz & 915MHz devices, it should work fine with 868MHz etc.
Pay attention to your local ISM band regulations and ensure that you are compliant with them, especially frequency,power levels and duty cycle requirements.
private class ApplicationSettings
{
[JsonProperty("AzureIoTHubDeviceConnectionString", Required = Required.Always)]
public string AzureIoTHubDeviceConnectionString { get; set; }
[JsonProperty("AzureIoTHubTransportType", Required = Required.Always)]
[JsonConverter(typeof(StringEnumConverter))]
public TransportType AzureIoTHubTransportType { get; internal set; }
[JsonProperty("SensorIDIsDeviceIDSensorID", Required = Required.Always)]
public bool SensorIDIsDeviceIDSensorID { get; set; }
// LoRa configuration parameters
[JsonProperty("Address", Required = Required.Always)]
public string Address { get; set; }
[DefaultValue(Rfm9XDevice.FrequencyDefault)]
[JsonProperty("Frequency", DefaultValueHandling = DefaultValueHandling.Populate)]
public double Frequency { get; set; }
// RegPaConfig
[DefaultValue(Rfm9XDevice.PABoostDefault)]
[JsonProperty("PABoost", DefaultValueHandling = DefaultValueHandling.Populate)]
public bool PABoost { get; set; }
[DefaultValue(Rfm9XDevice.RegPAConfigMaxPowerDefault)]
[JsonProperty("MaxPower", DefaultValueHandling = DefaultValueHandling.Populate)]
public byte MaxPower { get; set; }
[DefaultValue(Rfm9XDevice.RegPAConfigOutputPowerDefault)]
[JsonProperty("OutputPower", DefaultValueHandling = DefaultValueHandling.Populate)]
public byte OutputPower { get; set; }
// RegOcp
[DefaultValue(Rfm9XDevice.RegOcpDefault)]
[JsonProperty("OCPOn", DefaultValueHandling = DefaultValueHandling.Populate)]
public bool OCPOn { get; set; }
[DefaultValue(Rfm9XDevice.RegOcpOcpTrimDefault)]
[JsonProperty("OCPTrim", DefaultValueHandling = DefaultValueHandling.Populate)]
public byte OCPTrim { get; set; }
// RegLna
[DefaultValue(Rfm9XDevice.LnaGainDefault)]
[JsonProperty("LNAGain", DefaultValueHandling = DefaultValueHandling.Populate)]
[JsonConverter(typeof(StringEnumConverter))]
public Rfm9XDevice.RegLnaLnaGain LnaGain { get; set; }
[DefaultValue(Rfm9XDevice.LnaBoostDefault)]
[JsonProperty("LNABoost", DefaultValueHandling = DefaultValueHandling.Populate)]
public bool LNABoost { get; set; }
// RegModemConfig1
[DefaultValue(Rfm9XDevice.RegModemConfigBandwidthDefault)]
[JsonProperty("Bandwidth", DefaultValueHandling = DefaultValueHandling.Populate)]
[JsonConverter(typeof(StringEnumConverter))]
public Rfm9XDevice.RegModemConfigBandwidth Bandwidth { get; set; }
[DefaultValue(Rfm9XDevice.RegModemConfigCodingRateDefault)]
[JsonProperty("codingRate", DefaultValueHandling = DefaultValueHandling.Populate)]
[JsonConverter(typeof(StringEnumConverter))]
public Rfm9XDevice.RegModemConfigCodingRate CodingRate { get; set; }
[DefaultValue(Rfm9XDevice.RegModemConfigImplicitHeaderModeOnDefault)]
[JsonProperty("ImplicitHeaderModeOn", DefaultValueHandling = DefaultValueHandling.Populate)]
[JsonConverter(typeof(StringEnumConverter))]
public Rfm9XDevice.RegModemConfigImplicitHeaderModeOn ImplicitHeaderModeOn { get; set; }
// RegModemConfig2SpreadingFactor
[DefaultValue(Rfm9XDevice.RegModemConfig2SpreadingFactorDefault)]
[JsonProperty("SpreadingFactor", DefaultValueHandling = DefaultValueHandling.Populate)]
public Rfm9XDevice.RegModemConfig2SpreadingFactor SpreadingFactor { get; set; }
[DefaultValue(Rfm9XDevice.SymbolTimeoutDefault)]
[JsonProperty("SymbolTimeout", DefaultValueHandling = DefaultValueHandling.Populate)]
public byte SymbolTimeout { get; set; }
[DefaultValue(Rfm9XDevice.PreambleLengthDefault)]
[JsonProperty("PreambleLength", DefaultValueHandling = DefaultValueHandling.Populate)]
public byte PreambleLength { get; set; }
[DefaultValue(Rfm9XDevice.PayloadLengthDefault)]
[JsonProperty("PayloadLength", DefaultValueHandling = DefaultValueHandling.Populate)]
public byte PayloadLength { get; set; }
[DefaultValue(Rfm9XDevice.PayloadMaxLengthDefault)]
[JsonProperty("PayloadMaxLength", DefaultValueHandling = DefaultValueHandling.Populate)]
public byte PayloadMaxLength { get; set; }
[DefaultValue(Rfm9XDevice.FreqHoppingPeriodDefault)]
[JsonProperty("freqHoppingPeriod", DefaultValueHandling = DefaultValueHandling.Populate)]
public byte FreqHoppingPeriod { get; set; }
[DefaultValue(Rfm9XDevice.LowDataRateOptimizeDefault)]
[JsonProperty("LowDataRateOptimize", DefaultValueHandling = DefaultValueHandling.Populate)]
public bool LowDataRateOptimize { get; set; }
[DefaultValue(Rfm9XDevice.AgcAutoOnDefault)]
[JsonProperty("AgcAutoOn", DefaultValueHandling = DefaultValueHandling.Populate)]
public byte AgcAutoOn { get; set; }
[DefaultValue(Rfm9XDevice.ppmCorrectionDefault)]
[JsonProperty("PPMCorrection", DefaultValueHandling = DefaultValueHandling.Populate)]
public byte PpmCorrection { get; set; }
[DefaultValue(Rfm9XDevice.RegDetectOptimizeDectionOptimizeDefault)]
[JsonProperty("DetectionOptimize", DefaultValueHandling = DefaultValueHandling.Populate)]
public Rfm9XDevice.RegDetectOptimizeDectionOptimize DetectionOptimize { get; set; }
[DefaultValue(Rfm9XDevice.InvertIqDefault)]
[JsonProperty("InvertIQ", DefaultValueHandling = DefaultValueHandling.Populate)]
public bool InvertIQ { get; set; }
[DefaultValue(Rfm9XDevice.RegisterDetectionThresholdDefault)]
[JsonProperty("DetectionThreshold", DefaultValueHandling = DefaultValueHandling.Populate)]
public Rfm9XDevice.RegisterDetectionThreshold DetectionThreshold { get; set; }
[DefaultValue(Rfm9XDevice.RegSyncWordDefault)]
[JsonProperty("SyncWord", DefaultValueHandling = DefaultValueHandling.Populate)]
public byte SyncWord { get; set; }
}
Future planned enhancements include packed binary payloads, cloud to device messaging (push & queued), device twin support for the field gateway, Azure Device Provisioning Service (DPS) support, automagic device provisioning, support for other hardware platforms e.g. Dragonboard 410C (which has a TPM for provisioning), and automated updates.
If you want to uploading data to Azure IoT Central the connection string has to be generated using the Azure IoT DPS Symmetric Key Generator. The process of manually adding a real device is covered here.
Comments