LoraDOG

Helium powered IoT solution to track your best friends during Kennel services.

AdvancedFull instructions provided20 hours1,652

Things used in this project

Hardware components

Helium Developer Kit
Helium Developer Kit
×1
FiPy
Pycom FiPy
×1
Laptop as NodeRED Server
×1

Software apps and online services

Helium Console
Arduino IDE
Arduino IDE
Node-RED
Node-RED
AWS IoT
Amazon Web Services AWS IoT
AWS SNS
Amazon Web Services AWS SNS
AWS Lambda
Amazon Web Services AWS Lambda
AWS DynamoDB
Amazon Web Services AWS DynamoDB
Pymakr Plugin
Pycom Pymakr Plugin

Hand tools and fabrication machines

Apple iPad

Story

Read more

Schematics

Diagram

Code

Arduino Code

C/C++
Kit Code
#include <LSM6DSOSensor.h>
#include <LIS2DW12Sensor.h>
#include <LIS2MDLSensor.h>
#include <LPS22HHSensor.h>
#include <STTS751Sensor.h>
#include <HTS221Sensor.h>
#include "teseo_liv3f_class.h"
#include <ArduinoJson.h>
#include "LoRaWAN.h"

const char *devEui = "YOUR_DEVEUI";
const char *appEui = "YOUR_APPEUI";
const char *appKey = "YOUR_APPKEY";

TeseoLIV3F *gps;
int incomingByte;
GNSSParser_Data_t data;
char command[32] = {0};
char msg[256];
int cmdType = 0;
uint32_t t, s;
int tracked;
GPGGA_Info_t stored_positions[64];
int status = 0;
uint32_t stime = 0;
int waitType = 0;

#define MSG_SZ 256
#define waitForRequest 0
#define waitForAnswer 1

#ifdef ARDUINO_SAM_DUE
#define DEV_I2C Wire1
#elif defined(ARDUINO_ARCH_STM32)
#define DEV_I2C Wire
#elif defined(ARDUINO_ARCH_AVR)
#define DEV_I2C Wire
#else
#define DEV_I2C Wire
#endif

#ifdef ARDUINO_SAM_DUE
#include <avr/dtostrf.h>
#elif ARDUINO_STM32L0_B_L072Z_LRWAN1
#include <avr/dtostrf.h>
#endif

// Components
LSM6DSOSensor *AccGyr;
LIS2DW12Sensor *Acc2;
LIS2MDLSensor *Mag;
LPS22HHSensor *PressTemp;
HTS221Sensor *HumTemp;
STTS751Sensor *Temp3;

unsigned long time1;
unsigned long time2;

String MyJson="{";

void setup() {
  StaticJsonDocument<200> doc;
  // Led.
  pinMode(LED_BUILTIN, OUTPUT);
  // Initialize serial for output.
  Serial.begin(115200);  
  // Initialize I2C bus.
  DEV_I2C.begin();
  Serial1.begin(9600);
  Serial.println("Setup begin");
  //Create the device object passing to it the serial interface
  gps = new TeseoLIV3F(&Serial1, 7, 13);
  //Initialize the device
  gps->init();
  AccGyr = new LSM6DSOSensor (&DEV_I2C);
  AccGyr->Enable_X();
  AccGyr->Enable_G();
  Acc2 = new LIS2DW12Sensor (&DEV_I2C);
  Acc2->Enable_X();
  Mag = new LIS2MDLSensor (&DEV_I2C);
  Mag->Enable();
  PressTemp = new LPS22HHSensor(&DEV_I2C);
  PressTemp->Enable();
  HumTemp = new HTS221Sensor (&DEV_I2C);
  HumTemp->Enable();
  Temp3 = new STTS751Sensor (&DEV_I2C);
  Temp3->Enable();
  LoRaWAN.begin(US915);
  // Helium SubBand
  LoRaWAN.setSubBand(2);
  // Disable Adaptive Data Rate
  LoRaWAN.setADR(false);
  // Set Data Rate 1 - Max Payload 53 Bytes
  LoRaWAN.setDataRate(3);
  // Device IDs and Key
  LoRaWAN.joinOTAA(appEui, appKey, devEui);
  Serial.println("Start...");
  time1=millis();
}

void loop() {

   // Repeat at least 20 times per second this command
   gps->update();
   
  if((millis() - time1) > 30000)
  {  
  MyJson="{";
  printValidInfo();
    // Read humidity and temperature.
  float humidity = 0, temperature = 0;
  HumTemp->GetHumidity(&humidity);
  HumTemp->GetTemperature(&temperature);

  MyJson += "\"Hum\":";
  MyJson += String(humidity);
  MyJson += ",";

  // Read pressure and temperature.
  float pressure = 0, temperature2 = 0;
  PressTemp->GetPressure(&pressure);
  PressTemp->GetTemperature(&temperature2);

  MyJson += "\"Press\":";
  MyJson += String(pressure);
  MyJson += ",";

  //Read temperature
  float temperature3 = 0;
  Temp3->GetTemperature(&temperature3);

  float temp = (temperature + temperature2 + temperature3)/3;

  MyJson += "\"Temp\":";
  MyJson += String(temp);
  MyJson += ",";

  // Read accelerometer and gyroscope.
  int32_t accelerometer[3];
  int32_t gyroscope[3];
  AccGyr->Get_X_Axes(accelerometer);
  AccGyr->Get_G_Axes(gyroscope);

  MyJson += "\"Acc\":[";
  MyJson += String(accelerometer[0])+",";
  MyJson += String(accelerometer[1])+",";
  MyJson += String(accelerometer[2]);
  MyJson += "],";

  MyJson += "\"Gyro\":[";
  MyJson += String(gyroscope[0])+",";
  MyJson += String(gyroscope[1])+",";
  MyJson += String(gyroscope[2]);
  MyJson += "],";

  //Read accelerometer
  int32_t accelerometer2[3];
  Acc2->Get_X_Axes(accelerometer2);

  //Read magnetometer
  int32_t magnetometer[3];
  Mag->GetAxes(magnetometer);

  MyJson += "\"Mag\":[";
  MyJson += String(magnetometer[0])+",";
  MyJson += String(magnetometer[1])+",";
  MyJson += String(magnetometer[2]);
  MyJson += "]}";

  if (LoRaWAN.joined() && !LoRaWAN.busy())
    {   
        // Send Packet
        uint8_t payload[] ="";
        MyJson.getBytes(payload, sizeof(MyJson));
        LoRaWAN.sendPacket(1, payload, sizeof(payload));
        Serial.println("Data Sent");
    }
  time1=millis(); 
  Serial.println(MyJson);
  } 
}
void printValidInfo()
{
   data = gps->getData();
   if (data.gpgga_data.valid == 1)
   {
      int lat = (int) (data.gpgga_data.xyz.lat/100.0);
      int lat_mod = (int) (data.gpgga_data.xyz.lat) - (lat * 100);
      int lon = (int) (data.gpgga_data.xyz.lon/100.0);
      int lon_mod = (int) (data.gpgga_data.xyz.lon) - (lon * 100);
      char alt[7];
      char acc[5];
      dtostrf (data.gpgga_data.xyz.alt, 3, 2, alt);
      dtostrf (data.gpgga_data.acc, 4, 1, acc);
      snprintf(msg, MSG_SZ, "UTC:\t\t\t[ %02ld:%02ld:%02ld ]\r\n",
               data.gpgga_data.utc.hh,
               data.gpgga_data.utc.mm,
               data.gpgga_data.utc.ss);
      MyJson += "\"UTC\":[";
      MyJson += String(data.gpgga_data.utc.hh)+",";
      MyJson += String(data.gpgga_data.utc.mm)+",";
      MyJson += String(data.gpgga_data.utc.ss);
      MyJson += "],";

      snprintf(msg, MSG_SZ, "Latitude:\t\t[ %.02d' %.02d'' %c ]\r\n",
               lat,
               lat_mod,
               data.gpgga_data.xyz.ns);
      MyJson += "\"Lat\":";
      MyJson += String(lat)+"."+String(lat_mod);
      MyJson += ",";

      snprintf(msg, MSG_SZ, "Longitude:\t\t[ %.02d' %.02d'' %c ]\r\n",
               lon,
               lon_mod,
               data.gpgga_data.xyz.ew);
      MyJson += "\"Lon\":";
      MyJson += String(lon)+"."+String(lon_mod);
      MyJson += ",";
    
      snprintf(msg, MSG_SZ, "Satellites locked:\t[ %ld ]\r\n",
               data.gpgga_data.sats);

      snprintf(msg, MSG_SZ, "Position accuracy:\t[ %s ]\r\n",
               acc);

      snprintf(msg, MSG_SZ, "Altitude:\t\t[ %s%c ]\r\n",
               alt,
               (data.gpgga_data.xyz.mis + 32U));
      MyJson += "\"Alt\":";
      MyJson += String(alt);
      MyJson += ",";

      snprintf(msg, MSG_SZ, "Geoid infos:\t\t[ %ld%c ]\r\n",
               data.gpgga_data.geoid.height,
               data.gpgga_data.geoid.mis);

      snprintf(msg, MSG_SZ, "Diff update:\t\t[ %ld ]\r\n",
               data.gpgga_data.update);
   }
   else
   {
      Serial.print("Last position wasn't valid.\r\n\n");
   }
   Serial.print("\r\n\n");
}

Notification.js

JavaScript
Lambda
   // Load the AWS SDK
   var AWS = require("aws-sdk");
   // Set up the code to call when the Lambda function is invoked
   exports.handler = (event, context, callback) => {
      
   // thresholds
   
   // Sensor Datasheet

   //(10 G) Stunt Safety Limit
   var accel = 100 
   // WHO NOM Limits kPa
   var maxP = 82 
   var minP = 74 
   // Max 45 Degrees 
   var maxD = 45 
   // Normal sensor values on a flat surface
   var xnom=-3
   var ynom = -8
   var znom = 98
   // WHO max Teslas
   var maxMag = 100 
   // dartmouth college standard
   var maxTemp = 30 
   var minTemp = 15
   var maxH = 80
   var minH = 20
   
   // Function
   
   var eventText = JSON.parse(JSON.stringify(event, null, 2));
   var sensor;
   var value;

      if (eventText["Acc"] != undefined) {
      sensor="Accel"
      var x = Math.abs(parseInt(eventText["Acc"].split(",")[0])*0.1);
      var y = Math.abs(parseInt(eventText["Acc"].split(",")[1])*0.1);
      var z = Math.abs(parseInt(eventText["Acc"].split(",")[2])*0.1);
      value=eventText["Acc"]
      if(x > accel  || y > accel || z > accel )
      {
         var sns = new AWS.SNS();
         var params = {
         Message: "Excessive movement detected",
         TopicArn: "arn:aws:sns:us-east-1:xxxxxxxxxxxxx:YOURTOPIC"
         };
         sns.publish(params, context.done);
      }
   }
   else if (eventText["Gyro"] != undefined) {
      sensor="Gyro"
      var x = parseInt(eventText["Gyro"].split(",")[0])*0.1;
      var y = parseInt(eventText["Gyro"].split(",")[1])*0.1;
      var z = parseInt(eventText["Gyro"].split(",")[2])*0.1;
      value=eventText["Gyro"]
       if(Math.abs(x-xnom) > maxMag  || Math.abs(y-ynom) > maxMag || Math.abs(z-znom) > maxMag )
      {
         var sns = new AWS.SNS();
         var params = {
         Message: "Strange position detected",
         TopicArn: "arn:aws:sns:us-east-1:xxxxxxxxxxxxx:YOURTOPIC"
         };
         sns.publish(params, context.done);
      }
   }
   else if (eventText["Mag"] != undefined) {
      sensor="Magnet"
      var  x = Math.abs(parseInt(eventText["Mag"].split(",")[0])*0.1);
      var y = Math.abs(parseInt(eventText["Mag"].split(",")[1])*0.1);
      var z = Math.abs(parseInt(eventText["Mag"].split(",")[2])*0.1);
      value=eventText["Mag"]
      if(x > maxMag  || y > maxMag || z > maxMag )
      {
         var sns = new AWS.SNS();
         var params = {
         Message: "High magnetic field detected.",
         TopicArn: "arn:aws:sns:us-east-1:xxxxxxxxxxxxx:YOURTOPIC"
         };
         sns.publish(params, context.done);
      }
   }
   else if (eventText["Temp"] != undefined) {
      sensor="Temp"
      value=parseInt(eventText["Temp"])
      
     if(value > maxH)
      {
         var sns = new AWS.SNS();
         var params = {
         Message: "High Temperature Detected",
         TopicArn: "arn:aws:sns:us-east-1:xxxxxxxxxxxxx:YOURTOPIC"
         };
         sns.publish(params, context.done);
      }
      else if(value < minH)
      {
         var sns = new AWS.SNS();
         var params = {
         Message: "Low Temperature Detected",
         TopicArn: "arn:aws:sns:us-east-1:xxxxxxxxxxxxx:YOURTOPIC"
         };
         sns.publish(params, context.done);
      }
      
   }
   else if (eventText["Press"] != undefined) {
      sensor="Press"
      value=parseInt(eventText["Press"])
      if(value > maxP)
      {
         var sns = new AWS.SNS();
         var params = {
         Message: "High Pressure Detected",
         TopicArn: "arn:aws:sns:us-east-1:xxxxxxxxxxxxx:YOURTOPIC"
         };
         sns.publish(params, context.done);
      }
      else if(value < minP)
      {
         var sns = new AWS.SNS();
         var params = {
         Message: "Low Pressure Detected",
         TopicArn: "arn:aws:sns:us-east-1:xxxxxxxxxxxxx:YOURTOPIC"
         };
         sns.publish(params, context.done);
      }
   }
   else if (eventText["Hum"] != undefined) {
      sensor="Hum"
      value=parseInt(eventText["Hum"])
      if(value > maxH)
      {
         var sns = new AWS.SNS();
         var params = {
         Message: "High Humidity Detected",
         TopicArn: "arn:aws:sns:us-east-1:xxxxxxxxxxxxx:YOURTOPIC"
         };
         sns.publish(params, context.done);
      }
      else if(value < minH)
      {
         var sns = new AWS.SNS();
         var params = {
         Message: "Low Humidity Detected",
         TopicArn: "arn:aws:sns:us-east-1:xxxxxxxxxxxxx:YOURTOPIC"
         };
         sns.publish(params, context.done);
      }
   }
   else{
      // Nothing
   }
   return [sensor,x]
    
};

Pycom Code

MicroPython
#!/usr/bin/env python
#
# Copyright (c) 2019, Pycom Limited.
#
# This software is licensed under the GNU GPL version 3 or any
# later version, with permitted additional terms. For more information
# see the Pycom Licence v1.0 document supplied with this file, or
# available at https://www.pycom.io/opensource/licensing
#

""" LoPy LoRaWAN Nano Gateway configuration options """

import machine
import ubinascii

WIFI_MAC = ubinascii.hexlify(machine.unique_id()).upper()
print()
# Set  the Gateway ID to be the first 3 bytes of MAC address + 'FFFE' + last 3 bytes of MAC address
GATEWAY_ID = WIFI_MAC[:6] + "FFFE" + WIFI_MAC[6:12]

SERVER = 'YOUR_MINER'
PORT = 1680

NTP = "pool.ntp.org"
NTP_PERIOD_S = 3600

WIFI_SSID = 'YOUR_SSID'
WIFI_PASS = 'YOURPASS'

LORA_FREQUENCY = 904300000
LORA_GW_DR = "SF10BW125" # DR_0
LORA_NODE_DR = 0

Code and Repo

Credits

Victor Altamirano

Victor Altamirano

25 projects • 81 followers
I am a Biomedical engineer who likes to develop hardware and software solutions.
Alejandro Sanchez

Alejandro Sanchez

9 projects • 19 followers
I am a biomedical engineer working at Boston Scientific as a service engineer
JulietaAltamirano

JulietaAltamirano

2 projects • 3 followers
📍Currently in CO 🇺🇸 🧬 Physician 🥬 Vegetarian & animal lover 🐷

Comments