After a longish pause in development work on my nrf24L01 AdaFruit.IO and Azure IOT Hub field gateways I figured a client based on my port of the techfooninja nRF24 library to Wilderness Labs Meadow would be a good test.
This sample client is an Wilderness Labs Meadow with a Sensiron SHT31 Temperature & humidity sensor (supported by meadow foundation), and a generic nRF24L01 device connected with jumper cables.
Bill of materials (prices as at March 2020)
- Wilderness Labs Meadow 7F Micro device USD50
- Seeedstudio Temperature and Humidity Sensor(SHT31) USD11.90
- Seeedstudio 4 pin Male Jumper to Grove 4 pin Conversion CableĀ USD2.90
- 2.4G Wireless Module nRF24L01+PA USD9.90
The initial version of the code was pretty basic with limited error handling and no power conservation support.
namespace devMobile.IoT.FieldGateway.Client
{
using System;
using System.Text;
using System.Threading;
using Radios.RF24;
using Meadow;
using Meadow.Devices;
using Meadow.Foundation.Leds;
using Meadow.Foundation.Sensors.Atmospheric;
using Meadow.Hardware;
using Meadow.Peripherals.Leds;
public class MeadowClient : App<F7Micro, MeadowClient>
{
private const string BaseStationAddress = "Base1";
private const string DeviceAddress = "WLAB1";
private const byte nRF24Channel = 15;
private RF24 Radio = new RF24();
private readonly TimeSpan periodTime = new TimeSpan(0, 0, 60);
private readonly Sht31D sensor;
private readonly ILed Led;
public MeadowClient()
{
Led = new Led(Device, Device.Pins.OnboardLedGreen);
try
{
sensor = new Sht31D(Device.CreateI2cBus());
var config = new Meadow.Hardware.SpiClockConfiguration(
2000,
SpiClockConfiguration.Mode.Mode0);
ISpiBus spiBus = Device.CreateSpiBus(
Device.Pins.SCK,
Device.Pins.MOSI,
Device.Pins.MISO, config);
Radio.OnDataReceived += Radio_OnDataReceived;
Radio.OnTransmitFailed += Radio_OnTransmitFailed;
Radio.OnTransmitSuccess += Radio_OnTransmitSuccess;
Radio.Initialize(Device, spiBus, Device.Pins.D09, Device.Pins.D10, Device.Pins.D11);
//Radio.Address = Encoding.UTF8.GetBytes(Environment.MachineName);
Radio.Address = Encoding.UTF8.GetBytes(DeviceAddress);
Radio.Channel = nRF24Channel;
Radio.PowerLevel = PowerLevel.Low;
Radio.DataRate = DataRate.DR250Kbps;
Radio.IsEnabled = true;
Radio.IsAutoAcknowledge = true;
Radio.IsDyanmicAcknowledge = false;
Radio.IsDynamicPayload = true;
Console.WriteLine($"Address: {Encoding.UTF8.GetString(Radio.Address)}");
Console.WriteLine($"PowerLevel: {Radio.PowerLevel}");
Console.WriteLine($"IsAutoAcknowledge: {Radio.IsAutoAcknowledge}");
Console.WriteLine($"Channel: {Radio.Channel}");
Console.WriteLine($"DataRate: {Radio.DataRate}");
Console.WriteLine($"IsDynamicAcknowledge: {Radio.IsDyanmicAcknowledge}");
Console.WriteLine($"IsDynamicPayload: {Radio.IsDynamicPayload}");
Console.WriteLine($"IsEnabled: {Radio.IsEnabled}");
Console.WriteLine($"Frequency: {Radio.Frequency}");
Console.WriteLine($"IsInitialized: {Radio.IsInitialized}");
Console.WriteLine($"IsPowered: {Radio.IsPowered}");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
while (true)
{
sensor.Update();
Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss}-TX T:{sensor.Temperature:0.0}C H:{sensor.Humidity:0}%");
Led.IsOn = true;
string values = "T " + sensor.Temperature.ToString("F1") + ",H " + sensor.Humidity.ToString("F0");
// Stuff the 2 byte header ( payload type & deviceIdentifierLength ) + deviceIdentifier into payload
byte[] payload = new byte[1 + Radio.Address.Length + values.Length];
payload[0] = (byte)((1 << 4) | Radio.Address.Length);
Array.Copy(Radio.Address, 0, payload, 1, Radio.Address.Length);
Encoding.UTF8.GetBytes(values, 0, values.Length, payload, Radio.Address.Length + 1);
Radio.SendTo(Encoding.UTF8.GetBytes(BaseStationAddress), payload);
Thread.Sleep(periodTime);
}
}
private void Radio_OnDataReceived(byte[] data)
{
// Display as Unicode
string unicodeText = Encoding.UTF8.GetString(data);
Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss}-RX Unicode Length {0} Unicode Length {1} Unicode text {2}", data.Length, unicodeText.Length, unicodeText);
// display as hex
Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss}-RX Hex Length {data.Length} Payload {BitConverter.ToString(data)}");
}
private void Radio_OnTransmitSuccess()
{
Led.IsOn = false;
Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss}-TX Succeeded!");
}
private void Radio_OnTransmitFailed()
{
Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss}-TX failed!");
}
}
}
After sorting out power to the SHT31 (I had to push the jumper cable further into the back of the jumper cable plug). I could see temperature and humidity values getting uploaded to Adafruit.IO.
Adafruit.IO “automagically” provisions new feeds which is helpful when building a proof of concept (PoC)
I then modified the feed configuration to give it a user friendly name.
All up configuration took about 10 minutes.