Last week a package arrived from LowPowerLab with some Moteino0 devices and accessories . With this gear I have built yet another client for my Azure IoT Hub and AdaFruit.IOLoRa Field Gateways.
It took me a while longer that usual to get the Motenio working as the sketch setup call appeared to hang in DEBUG builds.
After staring at the code for a while I noticed that I hadn’t changed LoRa.dumpRegisters method parameter from Serial to SerialUSB. A couple of hours lost due to a dumb typo by me.
Now that the device is running well, I’ll look at reducing power consumption and splitting the the payload packing code into a library.
/*
Copyright ® 2018 November devMobile Software, All Rights Reserved
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE.
You can do what you want with this code, acknowledgment would be nice.
http://www.devmobile.co.nz
*/
#include <stdlib.h>
#include <avr/dtostrf.h>
#include <LoRa.h>
#include <TH02_dev.h>
//#define DEBUG
//#define DEBUG_TELEMETRY
//#define DEBUG_LORA
// LoRa field gateway configuration (these settings must match your field gateway)
const char FieldGatewayAddress[] = {"LoRaIoT1"};
const char DeviceAddress[] = {"Moteino01"};
const float FieldGatewayFrequency = 915000000.0;
const byte FieldGatewaySyncWord = 0x12 ;
// Payload configuration
const int ChipSelectPin = A2;
const int InterruptPin = 9;
const int ResetPin = -1;
// LoRa radio payload configuration
const byte SensorIdValueSeperator = ' ' ;
const byte SensorReadingSeperator = ',' ;
const int LoopSleepDelaySeconds = 10 ;
const byte PayloadSizeMaximum = 64 ;
byte payload[PayloadSizeMaximum];
byte payloadLength = 0 ;
void setup()
{
SerialUSB.begin(9600);
#ifdef DEBUG
while (!SerialUSB);
#endif
SerialUSB.println("Setup called");
SerialUSB.println("LoRa setup start");
// override the default chip select and reset pins
LoRa.setPins( ChipSelectPin, ResetPin, InterruptPin );
if (!LoRa.begin(FieldGatewayFrequency))
{
SerialUSB.println("LoRa begin failed");
while (true); // Drop into endless loop requiring restart
}
// Need to do this so field gateways pays attention to messsages from this device
LoRa.enableCrc();
LoRa.setSyncWord(FieldGatewaySyncWord);
#ifdef DEBUG_LORA
LoRa.dumpRegisters(USBSerial);
#endif
SerialUSB.println("LoRa Setup done.");
// Configure the Seeedstudio TH02 temperature & humidity sensor
SerialUSB.println("TH02 setup start");
TH02.begin();
delay(100);
SerialUSB.println("TH02 setup done");
PayloadHeader((byte*)FieldGatewayAddress,strlen(FieldGatewayAddress), (byte*)DeviceAddress, strlen(DeviceAddress));
SerialUSB.println("Setup done");
SerialUSB.println();
}
void loop()
{
float temperature ;
float humidity ;
SerialUSB.println("Loop called");
PayloadReset();
// Read the temperature & humidity & battery voltage values then display nicely
temperature = TH02.ReadTemperature();
SerialUSB.print("T:");
SerialUSB.print( temperature, 1 ) ;
SerialUSB.println( "C " ) ;
PayloadAdd( "T", temperature, 1);
humidity = TH02.ReadHumidity();
SerialUSB.print("H:");
SerialUSB.print( humidity, 0 ) ;
SerialUSB.println( "% " ) ;
PayloadAdd( "H", humidity, 0) ;
#ifdef DEBUG_TELEMETRY
SerialUSB.println();
SerialUSB.print( "RFM9X/SX127X Payload length:");
SerialUSB.print( payloadLength );
SerialUSB.println( " bytes" );
#endif
LoRa.beginPacket();
LoRa.write( payload, payloadLength );
LoRa.endPacket();
SerialUSB.println("Loop done");
SerialUSB.println();
delay(LoopSleepDelaySeconds * 1000l);
}
void PayloadHeader( byte *to, byte toAddressLength, byte *from, byte fromAddressLength)
{
byte addressesLength = toAddressLength + fromAddressLength ;
#ifdef DEBUG_TELEMETRY
SerialUSB.println("PayloadHeader- ");
SerialUSB.print( "To Address len:");
SerialUSB.print( toAddressLength );
SerialUSB.print( " From Address len:");
SerialUSB.print( fromAddressLength );
SerialUSB.print( " Addresses length:");
SerialUSB.print( addressesLength );
SerialUSB.println( );
#endif
payloadLength = 0 ;
// prepare the payload header with "To" Address length (top nibble) and "From" address length (bottom nibble)
payload[payloadLength] = (toAddressLength << 4) | fromAddressLength ;
payloadLength += 1;
// Copy the "To" address into payload
memcpy(&payload[payloadLength], to, toAddressLength);
payloadLength += toAddressLength ;
// Copy the "From" into payload
memcpy(&payload[payloadLength], from, fromAddressLength);
payloadLength += fromAddressLength ;
}
void PayloadAdd( const char *sensorId, float value, byte decimalPlaces)
{
byte sensorIdLength = strlen( sensorId ) ;
#ifdef DEBUG_TELEMETRY
SerialUSB.println("PayloadAdd-float ");
SerialUSB.print( "SensorId:");
SerialUSB.print( sensorId );
SerialUSB.print( " sensorIdLen:");
SerialUSB.print( sensorIdLength );
SerialUSB.print( " Value:");
SerialUSB.print( value, decimalPlaces );
SerialUSB.print( " payloadLength:");
SerialUSB.print( payloadLength);
#endif
memcpy( &payload[payloadLength], sensorId, sensorIdLength) ;
payloadLength += sensorIdLength ;
payload[ payloadLength] = SensorIdValueSeperator;
payloadLength += 1 ;
payloadLength += strlen( dtostrf(value, -1, decimalPlaces, (char *)&payload[payloadLength]));
payload[ payloadLength] = SensorReadingSeperator;
payloadLength += 1 ;
#ifdef DEBUG_TELEMETRY
SerialUSB.print( " payloadLength:");
SerialUSB.print( payloadLength);
SerialUSB.println( );
#endif
}
void PayloadAdd( const char *sensorId, int value )
{
byte sensorIdLength = strlen( sensorId ) ;
#ifdef DEBUG_TELEMETRY
SerialUSB.println("PayloadAdd-int ");
SerialUSB.print( "SensorId:");
SerialUSB.print( sensorId );
SerialUSB.print( " sensorIdLen:");
SerialUSB.print( sensorIdLength );
SerialUSB.print( " Value:");
SerialUSB.print( value );
SerialUSB.print( " payloadLength:");
SerialUSB.print( payloadLength);
#endif
memcpy( &payload[payloadLength], sensorId, sensorIdLength) ;
payloadLength += sensorIdLength ;
payload[ payloadLength] = SensorIdValueSeperator;
payloadLength += 1 ;
payloadLength += strlen( itoa( value,(char *)&payload[payloadLength],10));
payload[ payloadLength] = SensorReadingSeperator;
payloadLength += 1 ;
#ifdef DEBUG_TELEMETRY
SerialUSB.print( " payloadLength:");
SerialUSB.print( payloadLength);
SerialUSB.println( );
#endif
}
void PayloadAdd( const char *sensorId, unsigned int value )
{
byte sensorIdLength = strlen( sensorId ) ;
#ifdef DEBUG_TELEMETRY
SerialUSB.println("PayloadAdd-unsigned int ");
SerialUSB.print( "SensorId:");
SerialUSB.print( sensorId );
SerialUSB.print( " sensorIdLen:");
SerialUSB.print( sensorIdLength );
SerialUSB.print( " Value:");
SerialUSB.print( value );
SerialUSB.print( " payloadLength:");
SerialUSB.print( payloadLength);
#endif
memcpy( &payload[payloadLength], sensorId, sensorIdLength) ;
payloadLength += sensorIdLength ;
payload[ payloadLength] = SensorIdValueSeperator;
payloadLength += 1 ;
payloadLength += strlen( utoa( value,(char *)&payload[payloadLength],10));
payload[ payloadLength] = SensorReadingSeperator;
payloadLength += 1 ;
#ifdef DEBUG_TELEMETRY
SerialUSB.print( " payloadLength:");
SerialUSB.print( payloadLength);
SerialUSB.println( );
#endif
}
void PayloadReset()
{
byte fromAddressLength = payload[0] & 0xf ;
byte toAddressLength = payload[0] >> 4 ;
byte addressesLength = toAddressLength + fromAddressLength ;
payloadLength = addressesLength + 1;
#ifdef DEBUG_TELEMETRY
SerialUSB.println("PayloadReset- ");
SerialUSB.print( "To Address len:");
SerialUSB.print( toAddressLength );
SerialUSB.print( " From Address len:");
SerialUSB.print( fromAddressLength );
SerialUSB.print( " Addresses length:");
SerialUSB.print( addressesLength );
SerialUSB.println( );
#endif
}
Bill of materials (prices as at November 2018)