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)