Mithun Das
Published © GPL3+

VFence: Keep Your Pets and Kids Within Virtual Fence

Say NO to shock collars! Setup virtual fence on the app and keep your beloved pet within your reach. Track your kids while biking from app.

IntermediateProtipOver 1 day5,301

Things used in this project

Hardware components

Raspberry Pi 4 Model B
Raspberry Pi 4 Model B
Needed for DIY hotspot
×1
RAKwireless RAK2245 Pi HAT
Needed for DIY hotspot
×1
Heltec CubeCell GPS-6502
×2
SparkFun Triple Axis Accelerometer and Gyro Breakout - MPU-6050
SparkFun Triple Axis Accelerometer and Gyro Breakout - MPU-6050
×1
Solar Cockroach Vibrating Disc Motor
Brown Dog Gadgets Solar Cockroach Vibrating Disc Motor
×1
TP4056 Charging Module
×2
Rechargeable Battery, 3.7 V
Rechargeable Battery, 3.7 V
×2

Software apps and online services

Arduino IDE
Arduino IDE
balenaCloud
balenaCloud
AWS IoT
Amazon Web Services AWS IoT
AWS Lambda
Amazon Web Services AWS Lambda
AWS API Gateway
Amazon Web Services AWS API Gateway
AWS Cognito
Amazon Web Services AWS Cognito
AWS DynamoDB
Amazon Web Services AWS DynamoDB
AWS SDK
Amazon Web Services AWS SDK
Apple Swift for iOS
Helium Network

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)
10 Pc. Jumper Wire Kit, 5 cm Long
10 Pc. Jumper Wire Kit, 5 cm Long

Story

Read more

Custom parts and enclosures

helium-hotspot-case_lgCP9WqtIC.stl

pet-1_WnR9NIk5GC.stl

pet-2_3vEx7PLuKo.stl

helium-hotspot-lid_He7V2OizVy.stl

helium-hotspot-logo_8XIlnTSh5K.stl

bike-case-buttom_eALkp4x1XB.stl

bike-case-top_zpt608ukxJ.stl

Code

LoRaWan_APP.cpp

Arduino
I added few more methods to display custom message on OLED
#include <LoRaWan_APP.h>
#if(LoraWan_RGB==1)
#include "CubeCell_NeoPixel.h"
CubeCell_NeoPixel pixels(1, RGB, NEO_GRB + NEO_KHZ800);
#endif

#if REGION_EU868
#include "RegionEU868.h"
#endif

#if REGION_EU433
#include "RegionEU433.h"
#endif

#ifdef CubeCell_BoardPlus
#include <Wire.h>  
#include "cubecell_SH1107Wire.h"

  SH1107Wire  display(0x3c, 500000, I2C_NUM_0,GEOMETRY_128_64,GPIO10); // addr , freq , i2c group , resolution , rst

  uint8_t ifDisplayAck=0;
  uint8_t isDispayOn=0;
#endif

#ifdef CubeCell_GPS
#include <Wire.h>  
#include "cubecell_SSD1306Wire.h"

  SSD1306Wire  display(0x3c, 500000, I2C_NUM_0,GEOMETRY_128_64,GPIO10 ); // addr , freq , i2c group , resolution , rst

  uint8_t ifDisplayAck=0;
  uint8_t isDispayOn=0;
#endif

/*!
 * Default datarate
 * was DR_0
 */
#define LORAWAN_DEFAULT_DATARATE                    DR_1

/*!
 * User application data size
 */
uint8_t appDataSize = 4;

/*!
 * User application data
 */
uint8_t appData[LORAWAN_APP_DATA_MAX_SIZE];


/*!
 * Defines the application data transmission duty cycle
 */
uint32_t txDutyCycleTime ;

/*!
 * Timer to handle the application data transmission duty cycle
 */
static TimerEvent_t TxNextPacketTimer;

/*!
 * PassthroughMode mode enable/disable. don't modify it here. 
 * when use PassthroughMode, set it true in app.ino , Reference the example PassthroughMode.ino 
 */
bool passthroughMode = false;

/*!
 * when use PassthroughMode, Mode_LoraWan to set use lora or lorawan mode . don't modify it here. 
 * it is used to set mode lora/lorawan in PassthroughMode.
 */
bool modeLoraWan = true;

/*!
 * Indicates if a new packet can be sent
 */
static bool nextTx = true;


enum eDeviceState_LoraWan deviceState;

/*
 * Default OLED display 
 */ 
bool useOLED = true;


/*!
 * \brief   Prepares the payload of the frame
 *
 * \retval  [0: frame could be send, 1: error]
 */
bool SendFrame( void )
{
	lwan_dev_params_update();
	
	McpsReq_t mcpsReq;
	LoRaMacTxInfo_t txInfo;

	if( LoRaMacQueryTxPossible( appDataSize, &txInfo ) != LORAMAC_STATUS_OK )
	{
		// Send empty frame in order to flush MAC commands
		mcpsReq.Type = MCPS_UNCONFIRMED;
		mcpsReq.Req.Unconfirmed.fBuffer = NULL;
		mcpsReq.Req.Unconfirmed.fBufferSize = 0;
		mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE;
	}
	else
	{
		if( isTxConfirmed == false )
		{
			printf("unconfirmed uplink sending ...\r\n");
			mcpsReq.Type = MCPS_UNCONFIRMED;
			mcpsReq.Req.Unconfirmed.fPort = appPort;
			mcpsReq.Req.Unconfirmed.fBuffer = appData;
			mcpsReq.Req.Unconfirmed.fBufferSize = appDataSize;
			mcpsReq.Req.Unconfirmed.Datarate = LORAWAN_DEFAULT_DATARATE;
		}
		else
		{
			printf("confirmed uplink sending ...\r\n");
			mcpsReq.Type = MCPS_CONFIRMED;
			mcpsReq.Req.Confirmed.fPort = appPort;
			mcpsReq.Req.Confirmed.fBuffer = appData;
			mcpsReq.Req.Confirmed.fBufferSize = appDataSize;
			mcpsReq.Req.Confirmed.NbTrials = confirmedNbTrials;
			mcpsReq.Req.Confirmed.Datarate = LORAWAN_DEFAULT_DATARATE;
		}
	}
	if( LoRaMacMcpsRequest( &mcpsReq ) == LORAMAC_STATUS_OK )
	{
		return false;
	}
	return true;
}

/*!
 * \brief Function executed on TxNextPacket Timeout event
 */
static void OnTxNextPacketTimerEvent( void )
{
	MibRequestConfirm_t mibReq;
	LoRaMacStatus_t status;

	TimerStop( &TxNextPacketTimer );

	mibReq.Type = MIB_NETWORK_JOINED;
	status = LoRaMacMibGetRequestConfirm( &mibReq );

	if( status == LORAMAC_STATUS_OK )
	{
		if( mibReq.Param.IsNetworkJoined == true )
		{
			deviceState = DEVICE_STATE_SEND;
			nextTx = true;
		}
		else
		{
			// Network not joined yet. Try to join again
			MlmeReq_t mlmeReq;
			mlmeReq.Type = MLME_JOIN;
			mlmeReq.Req.Join.DevEui = devEui;
			mlmeReq.Req.Join.AppEui = appEui;
			mlmeReq.Req.Join.AppKey = appKey;
			mlmeReq.Req.Join.NbTrials = 1;

			if( LoRaMacMlmeRequest( &mlmeReq ) == LORAMAC_STATUS_OK )
			{
				deviceState = DEVICE_STATE_SLEEP;
			}
			else
			{
				deviceState = DEVICE_STATE_CYCLE;
			}
		}
	}
}

/*!
 * \brief   MCPS-Confirm event function
 *
 * \param   [IN] mcpsConfirm - Pointer to the confirm structure,
 *               containing confirm attributes.
 */
static void McpsConfirm( McpsConfirm_t *mcpsConfirm )
{
	if( mcpsConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
	{
		switch( mcpsConfirm->McpsRequest )
		{
			case MCPS_UNCONFIRMED:
			{
				// Check Datarate
				// Check TxPower
				break;
			}
			case MCPS_CONFIRMED:
			{
				// Check Datarate
				// Check TxPower
				// Check AckReceived
				// Check NbTrials
				break;
			}
			case MCPS_PROPRIETARY:
			{
				break;
			}
			default:
				break;
		}
	}
	nextTx = true;
}

#if(LoraWan_RGB==1)
void turnOnRGB(uint32_t color,uint32_t time)
{
	uint8_t red,green,blue;
	red=(uint8_t)(color>>16);
	green=(uint8_t)(color>>8);
	blue=(uint8_t)color;
	pinMode(Vext,OUTPUT);
	digitalWrite(Vext,LOW); //SET POWER
	delay(1);
	pixels.begin(); // INITIALIZE RGB strip object (REQUIRED)
	pixels.clear(); // Set all pixel colors to 'off'
	pixels.setPixelColor(0, pixels.Color(red, green, blue));
	pixels.show();   // Send the updated pixel colors to the hardware.
	if(time>0)
	{
		delay(time);
	}
}

void turnOffRGB(void)
{
	turnOnRGB(0,0);
#if defined(CubeCell_BoardPlus)||defined(CubeCell_GPS)
	if(isDispayOn == 0)
	{
		digitalWrite(Vext,HIGH);
	}
#else
	digitalWrite(Vext,HIGH);
#endif
}
#endif

/*  get the BatteryVoltage in mV. */
uint16_t getBatteryVoltage(void)
{
	pinMode(VBAT_ADC_CTL,OUTPUT);
	digitalWrite(VBAT_ADC_CTL,LOW);
	uint16_t volt=analogRead(ADC)*2;
	digitalWrite(VBAT_ADC_CTL,HIGH);
	return volt;
}


void __attribute__((weak)) downLinkDataHandle(McpsIndication_t *mcpsIndication)
{
	printf("+REV DATA:%s,RXSIZE %d,PORT %d\r\n",mcpsIndication->RxSlot?"RXWIN2":"RXWIN1",mcpsIndication->BufferSize,mcpsIndication->Port);
	printf("+REV DATA:");
	for(uint8_t i=0;i<mcpsIndication->BufferSize;i++)
	{
		printf("%02X",mcpsIndication->Buffer[i]);
	}
	printf("\r\n");
}

/*!
 * \brief   MCPS-Indication event function
 *
 * \param   [IN] mcpsIndication - Pointer to the indication structure,
 *               containing indication attributes.
 */
static void McpsIndication( McpsIndication_t *mcpsIndication )
{
	if( mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK )
	{
		return;
	}
#if defined(CubeCell_BoardPlus)||defined(CubeCell_GPS)
	ifDisplayAck=1;
#endif
	printf( "receive data: rssi = %d, snr = %d, datarate = %d\r\n", mcpsIndication->Rssi, (int)mcpsIndication->Snr,(int)mcpsIndication->RxDatarate);

#if (LoraWan_RGB==1)
	turnOnRGB(COLOR_RECEIVED, 200);
	turnOffRGB();
#endif

	switch( mcpsIndication->McpsIndication )
	{
		case MCPS_UNCONFIRMED:
		{
			break;
		}
		case MCPS_CONFIRMED:
		{
			break;
		}
		case MCPS_PROPRIETARY:
		{
			break;
		}
		case MCPS_MULTICAST:
		{
			break;
		}
		default:
			break;
	}

	// Check Multicast
	// Check Port
	// Check Datarate
	// Check FramePending
	if( mcpsIndication->FramePending == true )
	{
		// The server signals that it has pending data to be sent.
		// We schedule an uplink as soon as possible to flush the server.
		OnTxNextPacketTimerEvent( );
	}
	// Check Buffer
	// Check BufferSize
	// Check Rssi
	// Check Snr
	// Check RxSlot
	if( mcpsIndication->RxData == true )
	{
		downLinkDataHandle(mcpsIndication);
	}
}

/*!
 * \brief   MLME-Confirm event function
 *
 * \param   [IN] mlmeConfirm - Pointer to the confirm structure,
 *               containing confirm attributes.
 */
static void MlmeConfirm( MlmeConfirm_t *mlmeConfirm )
{
	switch( mlmeConfirm->MlmeRequest )
	{
		case MLME_JOIN:
		{
			if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
			{

#if (LoraWan_RGB==1)
				turnOnRGB(COLOR_JOINED,500);
				turnOffRGB();
#endif
#if defined(CubeCell_BoardPlus)||defined(CubeCell_GPS)
				if(isDispayOn)
				{
					LoRaWAN.displayJoined();
				}
#endif
				printf("joined\r\n");
				
				//in PassthroughMode,do nothing while joined
				if(passthroughMode == false)
				{
					// Status is OK, node has joined the network
					deviceState = DEVICE_STATE_SEND;
				}
			}
			else
			{
				uint32_t rejoin_delay = 30000;
				printf("join failed, join again at 30s later\r\n");
				TimerSetValue( &TxNextPacketTimer, rejoin_delay );
				TimerStart( &TxNextPacketTimer );
			}
			break;
		}
		case MLME_LINK_CHECK:
		{
			if( mlmeConfirm->Status == LORAMAC_EVENT_INFO_STATUS_OK )
			{
				// Check DemodMargin
				// Check NbGateways
			}
			break;
		}
		default:
			break;
	}
	nextTx = true;
}

/*!
 * \brief   MLME-Indication event function
 *
 * \param   [IN] mlmeIndication - Pointer to the indication structure.
 */
static void MlmeIndication( MlmeIndication_t *mlmeIndication )
{
	switch( mlmeIndication->MlmeIndication )
	{
		case MLME_SCHEDULE_UPLINK:
		{// The MAC signals that we shall provide an uplink as soon as possible
			OnTxNextPacketTimerEvent( );
			break;
		}
		default:
			break;
	}
}


void lwan_dev_params_update( void )
{
#ifdef REGION_EU868
	LoRaMacChannelAdd( 3, ( ChannelParams_t )EU868_LC4 );
	LoRaMacChannelAdd( 4, ( ChannelParams_t )EU868_LC5 );
	LoRaMacChannelAdd( 5, ( ChannelParams_t )EU868_LC6 );
	LoRaMacChannelAdd( 6, ( ChannelParams_t )EU868_LC7 );
	LoRaMacChannelAdd( 7, ( ChannelParams_t )EU868_LC8 );
#endif

#ifdef REGION_EU433
		LoRaMacChannelAdd( 3, ( ChannelParams_t )EU433_LC4 );
		LoRaMacChannelAdd( 4, ( ChannelParams_t )EU433_LC5 );
		LoRaMacChannelAdd( 5, ( ChannelParams_t )EU433_LC6 );
		LoRaMacChannelAdd( 6, ( ChannelParams_t )EU433_LC7 );
		LoRaMacChannelAdd( 7, ( ChannelParams_t )EU433_LC8 );
#endif

	MibRequestConfirm_t mibReq;

	mibReq.Type = MIB_CHANNELS_DEFAULT_MASK;
	mibReq.Param.ChannelsMask = userChannelsMask;
	LoRaMacMibSetRequestConfirm(&mibReq);

	mibReq.Type = MIB_CHANNELS_MASK;
	mibReq.Param.ChannelsMask = userChannelsMask;
	LoRaMacMibSetRequestConfirm(&mibReq);
}

uint8_t BoardGetBatteryLevel()
{
	int8 batlevel = ((getBatteryVoltage()-3.7)/(4.2-3.7))*100;
	return batlevel;
}

LoRaMacPrimitives_t LoRaMacPrimitive;
LoRaMacCallback_t LoRaMacCallback;

void LoRaWanClass::init(DeviceClass_t lorawanClass,LoRaMacRegion_t region)
{
	Serial.print("\r\nLoRaWAN ");
	switch(region)
	{
		case LORAMAC_REGION_AS923:
			Serial.print("AS923");
			break;
		case LORAMAC_REGION_AU915:
			Serial.print("AU915");
			break;
		case LORAMAC_REGION_CN470:
			Serial.print("CN470");
			break;
		case LORAMAC_REGION_CN779:
			Serial.print("CN779");
			break;
		case LORAMAC_REGION_EU433:
			Serial.print("EU433");
			break;
		case LORAMAC_REGION_EU868:
			Serial.print("EU868");
			break;
		case LORAMAC_REGION_KR920:
			Serial.print("KR920");
			break;
		case LORAMAC_REGION_IN865:
			Serial.print("IN865");
			break;
		case LORAMAC_REGION_US915:
			Serial.print("US915");
			break;
		case LORAMAC_REGION_US915_HYBRID:
			Serial.print("US915_HYBRID ");
			break;
	}
	Serial.printf(" Class %X start!\r\n\r\n",loraWanClass+10);

	MibRequestConfirm_t mibReq;

	LoRaMacPrimitive.MacMcpsConfirm = McpsConfirm;
	LoRaMacPrimitive.MacMcpsIndication = McpsIndication;
	LoRaMacPrimitive.MacMlmeConfirm = MlmeConfirm;
	LoRaMacPrimitive.MacMlmeIndication = MlmeIndication;
	LoRaMacCallback.GetBatteryLevel = BoardGetBatteryLevel;
	LoRaMacCallback.GetTemperatureLevel = NULL;
	LoRaMacInitialization( &LoRaMacPrimitive, &LoRaMacCallback,region);
	TimerStop( &TxNextPacketTimer );
	TimerInit( &TxNextPacketTimer, OnTxNextPacketTimerEvent );

	mibReq.Type = MIB_ADR;
	mibReq.Param.AdrEnable = loraWanAdr;
	LoRaMacMibSetRequestConfirm( &mibReq );

	mibReq.Type = MIB_PUBLIC_NETWORK;
	mibReq.Param.EnablePublicNetwork = LORAWAN_PUBLIC_NETWORK;
	LoRaMacMibSetRequestConfirm( &mibReq );

	lwan_dev_params_update();

	deviceState = DEVICE_STATE_JOIN;
}


void LoRaWanClass::join()
{
	if( overTheAirActivation )
	{
		Serial.print("joining...");
		MlmeReq_t mlmeReq;
		
		mlmeReq.Type = MLME_JOIN;

		mlmeReq.Req.Join.DevEui = devEui;
		mlmeReq.Req.Join.AppEui = appEui;
		mlmeReq.Req.Join.AppKey = appKey;
		mlmeReq.Req.Join.NbTrials = 1;

		if( LoRaMacMlmeRequest( &mlmeReq ) == LORAMAC_STATUS_OK )
		{
			deviceState = DEVICE_STATE_SLEEP;
		}
		else
		{
			deviceState = DEVICE_STATE_CYCLE;
		}
	}
	else
	{
		MibRequestConfirm_t mibReq;

		mibReq.Type = MIB_NET_ID;
		mibReq.Param.NetID = LORAWAN_NETWORK_ID;
		LoRaMacMibSetRequestConfirm( &mibReq );

		mibReq.Type = MIB_DEV_ADDR;
		mibReq.Param.DevAddr = devAddr;
		LoRaMacMibSetRequestConfirm( &mibReq );

		mibReq.Type = MIB_NWK_SKEY;
		mibReq.Param.NwkSKey = nwkSKey;
		LoRaMacMibSetRequestConfirm( &mibReq );

		mibReq.Type = MIB_APP_SKEY;
		mibReq.Param.AppSKey = appSKey;
		LoRaMacMibSetRequestConfirm( &mibReq );

		mibReq.Type = MIB_NETWORK_JOINED;
		mibReq.Param.IsNetworkJoined = true;
		LoRaMacMibSetRequestConfirm( &mibReq );
		
		deviceState = DEVICE_STATE_SEND;
	}
}

void LoRaWanClass::send()
{
	if( nextTx == true )
	{
		MibRequestConfirm_t mibReq;
		mibReq.Type = MIB_DEVICE_CLASS;
		LoRaMacMibGetRequestConfirm( &mibReq );

		if(loraWanClass != mibReq.Param.Class)
		{
			mibReq.Param.Class = loraWanClass;
			LoRaMacMibSetRequestConfirm( &mibReq );
		}
		
		nextTx = SendFrame( );
	}
}

void LoRaWanClass::cycle(uint32_t dutyCycle)
{
	TimerSetValue( &TxNextPacketTimer, dutyCycle );
	TimerStart( &TxNextPacketTimer );
}

void LoRaWanClass::sleep()
{
	lowPowerHandler( );
	// Process Radio IRQ
	Radio.IrqProcess( );
}

void LoRaWanClass::ifskipjoin()
{
//if saved net info is OK in lorawan mode, skip join.
	if(checkNetInfo()&&modeLoraWan){
		Serial.println();
		if(passthroughMode==false)
		{
			Serial.println("Wait 3s for user key to rejoin network");
			uint16_t i=0;
			pinMode(GPIO7,INPUT);
			while(i<=3000)
			{
				if(digitalRead(GPIO7)==LOW)//if user key down, rejoin network;
				{
					netInfoDisable();
					pinMode(GPIO7,OUTPUT);
					digitalWrite(GPIO7,HIGH);
					return;
				}
				delay(1);
				i++;
			}
			pinMode(GPIO7,OUTPUT);
			digitalWrite(GPIO7,HIGH);
		}
#if(AT_SUPPORT)
		getDevParam();
#endif

		init(loraWanClass,loraWanRegion);
		getNetInfo();
		if(passthroughMode==false){
			Serial.println("User key not detected,Use reserved Net");
		}
		else{
			Serial.println("Use reserved Net");
		}
		if(passthroughMode==false)
		{
			int32_t temp=randr(0,appTxDutyCycle);
			Serial.println();
			Serial.printf("Next packet send %d ms later(random time from 0 to APP_TX_DUTYCYCLE)\r\n",temp);
			Serial.println();
			cycle(temp);//send packet in a random time to avoid network congestion.
		}
		deviceState = DEVICE_STATE_SLEEP;
	}
}

#if defined(CubeCell_BoardPlus)||defined(CubeCell_GPS)
void LoRaWanClass::displayJoining()
{
	if(useOLED){
		display.setFont(ArialMT_Plain_16);
		display.setTextAlignment(TEXT_ALIGN_CENTER);
		display.clear();
		display.drawString(58, 22, "JOINING...");
		display.display();
	}
	
}
void LoRaWanClass::displayJoined()
{
	if(useOLED){
		display.clear();
		display.drawString(64, 22, "JOINED");
		display.display();
		delay(1000);
	}
	
}
void LoRaWanClass::displaySending()
{
    isDispayOn = 1;
	digitalWrite(Vext,LOW);
	display.init();
	display.setFont(ArialMT_Plain_16);
	display.setTextAlignment(TEXT_ALIGN_CENTER);
	display.clear();
	display.drawString(58, 22, "SENDING...");
	display.display();
	delay(1000);
}
void LoRaWanClass::displayAck()
{
    if(ifDisplayAck==0)
    {
    	return;
    }
    ifDisplayAck--;
	display.clear();
	display.drawString(64, 22, "ACK RECEIVED");
	if(loraWanClass==CLASS_A)
	{
		display.setFont(ArialMT_Plain_10);
		display.setTextAlignment(TEXT_ALIGN_LEFT);
		display.drawString(28, 50, "Into deep sleep in 2S");
	}
	display.display();
	if(loraWanClass==CLASS_A)
	{
		delay(2000);
		isDispayOn = 0;
		digitalWrite(Vext,HIGH);
		display.stop();
	}
}
void LoRaWanClass::displayMcuInit()
{
	isDispayOn = 1;
	digitalWrite(Vext,LOW);
	display.init();
	display.setFont(ArialMT_Plain_16);
	display.setTextAlignment(TEXT_ALIGN_CENTER);
	display.clear();
	display.drawString(64, 11, "LORAWAN");
	display.drawString(64, 33, "STARTING");
	display.display();
	delay(2000);
}

void LoRaWanClass::displayMessage(String lat, String lon, String speed,String move)
{
	display.init();
	display.setFont(ArialMT_Plain_16);
	display.setTextAlignment(TEXT_ALIGN_LEFT);
	display.clear();
	display.drawString(5, 5, lat);
	display.drawString(5, 20, lon);
	display.drawString(5, 35, speed);
	display.drawString(5, 50, move);
	display.display();
	delay(2000);
}

void LoRaWanClass::displayAlert(String message)
{
	display.init();
	display.setFont(ArialMT_Plain_24);
	display.setTextAlignment(TEXT_ALIGN_LEFT);
	display.clear();
	display.drawString(25, 10, "OUT OF");
	display.drawString(30, 35, "FENCE");
	
	display.display();
	delay(2000);
}

void LoRaWanClass::displaySpeed(String speed, String extra)
{
	display.init();
	
	display.setTextAlignment(TEXT_ALIGN_LEFT);
	display.clear();
	display.setFont(ArialMT_Plain_24);
	display.drawString(50, 18, speed);
	display.setFont(ArialMT_Plain_10);
	display.drawString(30, 45, "SPEED (MPH)");
	display.setFont(ArialMT_Plain_10);
	display.drawString(0, 0, extra);

	display.display();
	delay(2000);
}



#endif

LoRaWanClass LoRaWAN;

LoRaWan_APP.h

Arduino
#ifndef LoRaWan_APP_H
#define LoRaWan_APP_H

#include <stdio.h>
#include "utilities.h"
#include "board.h"
#include "gpio.h"
#include "LoRaMac.h"
#include "Commissioning.h"
#include "hw.h"
#include "Region.h"
#include "low_power.h"
#include "spi-board.h"
#include "rtc-board.h"
#include "asr_timer.h"
#include "sx126x.h"
#include "board-config.h"
#include "hw_conf.h"
#include <uart_port.h>
#include "AT_Command.h"
#include <HardwareSerial.h>

extern uint8_t devEui[];
extern uint8_t appEui[];
extern uint8_t appKey[];
extern uint8_t nwkSKey[];
extern uint8_t appSKey[];
extern uint32_t devAddr;
extern uint8_t appData[LORAWAN_APP_DATA_MAX_SIZE];
extern uint8_t appDataSize;
extern uint8_t appPort;
extern uint32_t txDutyCycleTime;
extern bool overTheAirActivation;
extern LoRaMacRegion_t loraWanRegion;
extern bool loraWanAdr;
extern bool isTxConfirmed;
extern uint32_t appTxDutyCycle;
extern DeviceClass_t loraWanClass;
extern bool passthroughMode;
extern uint8_t confirmedNbTrials;
extern bool modeLoraWan;
extern bool keepNet;
extern bool IsLoRaMacNetworkJoined;
extern uint16_t userChannelsMask[6];
extern bool useOLED;

/*!
 * Defines a random delay for application data transmission duty cycle. 1s,
 * value in [ms].
 */
#define APP_TX_DUTYCYCLE_RND                        1000

extern enum eDeviceState_LoraWan deviceState;

class LoRaWanClass{
public:
  void init(DeviceClass_t lorawanClass,LoRaMacRegion_t region);
  void join();
  void send();
  void cycle(uint32_t dutyCycle);
  void sleep();
  void ifskipjoin();

#if defined(CubeCell_BoardPlus)||defined(CubeCell_GPS)
  void displayJoining();
  void displayJoined();
  void displaySending();
  void displayAck();
  void displayMcuInit();
  void displayMessage(String lat,String lon, String speed,String move);
  void displayAlert(String message);
  void displaySpeed(String speed,String extra);
  
#endif
};


extern "C" bool SendFrame( void );
extern "C" void turnOnRGB(uint32_t color,uint32_t time);
extern "C" void turnOffRGB(void);
extern "C" uint16_t getBatteryVoltage(void);
extern "C" bool checkUserAt(char * cmd, char * content);
extern "C" void downLinkDataHandle(McpsIndication_t *mcpsIndication);
extern "C" void lwan_dev_params_update( void );


extern LoRaWanClass LoRaWAN;

#endif

Bike_Device.ino

Arduino
#include "LoRaWan_APP.h"
#include "Arduino.h"
#include <ArduinoJson.h>
#include <Wire.h>
#include "GPS_Air530.h"
#include <MPU9250.h>
#include "CubeCell_NeoPixel.h"

#define VIBRATE GPIO7

CubeCell_NeoPixel pixels(1, RGB, NEO_GRB + NEO_KHZ800);

/*
   Pet device has no display to increase battery life
*/

/*
   set LoraWan_RGB to Active,the RGB active in loraWan
   RGB red means sending;
   RGB purple means joined done;
   RGB blue means RxWindow1;
   RGB yellow means RxWindow2;
   RGB green means received done;
*/

/* OTAA para*/
uint8_t devEui[] = {  };
uint8_t appEui[] = {  };
uint8_t appKey[] = {  };

/* ABP para*/
uint8_t nwkSKey[] = { 0x15, 0xb1, 0xd0, 0xef, 0xa4, 0x63, 0xdf, 0xbe, 0x3d, 0x11, 0x18, 0x1e, 0x1e, 0xc7, 0xda, 0x85 };
uint8_t appSKey[] = { 0xd7, 0x2c, 0x78, 0x75, 0x8c, 0xdc, 0xca, 0xbf, 0x55, 0xee, 0x4a, 0x77, 0x8d, 0x16, 0xef, 0x67 };
uint32_t devAddr =  ( uint32_t )0x007e6ae1;

/*LoraWan channelsmask, default channels 0-7*/
uint16_t userChannelsMask[6] = { 0xFF00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 };


/*LoraWan region, select in arduino IDE tools*/
LoRaMacRegion_t loraWanRegion = ACTIVE_REGION;

/*LoraWan Class, Class A and Class C are supported*/
DeviceClass_t  loraWanClass = LORAWAN_CLASS;

/*the application data transmission duty cycle.  value in [ms].*/
uint32_t appTxDutyCycle = 15000;
uint32_t appTxDutyCycle2 = 120000;

/*OTAA or ABP*/
bool overTheAirActivation = LORAWAN_NETMODE;

/*ADR enable*/
bool loraWanAdr = LORAWAN_ADR;

/* set LORAWAN_Net_Reserve ON, the node could save the network info to flash, when node reset not need to join again */
bool keepNet = LORAWAN_NET_RESERVE;

/* Indicates if the node is sending confirmed or unconfirmed messages */
bool isTxConfirmed = LORAWAN_UPLINKMODE;

/* Application port */
uint8_t appPort = 2;
/*!
  Number of trials to transmit the frame, if the LoRaMAC layer did not
  receive an acknowledgment. The MAC performs a datarate adaptation,
  according to the LoRaWAN Specification V1.0.2, chapter 18.4, according
  to the following table:

  Transmission nb | Data Rate
  ----------------|-----------
  1 (first)       | DR
  2               | DR
  3               | max(DR-1,0)
  4               | max(DR-1,0)
  5               | max(DR-2,0)
  6               | max(DR-2,0)
  7               | max(DR-3,0)
  8               | max(DR-3,0)

  Note, that if NbTrials is set to 1 or 2, the MAC will not decrease
  the datarate, in case the LoRaMAC layer did not receive an acknowledgment
*/
uint8_t confirmedNbTrials = 4;



const size_t capacity = JSON_OBJECT_SIZE(6) + 10;
DynamicJsonDocument payload(capacity);

bool OL = false;
float  lastLat = 0, lastLon = 0, lastSpeed = 0;

int count = 0;
int fall = 0;

MPU9250 mySensor;
float gX, gY, gZ, gSqrt, mDirection, mX, mY, mZ;
int aX, aY, aZ, aSqrt;

int fracPart(double val, int n)
{
  return (int)((val - (int)(val)) * pow(10, n));
}

/* Prepares the payload of the frame */
static void prepareTxFrame( uint8_t port )
{
  /*appData size is LORAWAN_APP_DATA_MAX_SIZE which is defined in "commissioning.h".
    appDataSize max value is LORAWAN_APP_DATA_MAX_SIZE.
    if enabled AT, don't modify LORAWAN_APP_DATA_MAX_SIZE, it may cause system hanging or failure.
    if disabled AT, LORAWAN_APP_DATA_MAX_SIZE can be modified, the max value is reference to lorawan region and SF.
    for example, if use REGION_CN470,
    the max value for different DR can be found in MaxPayloadOfDatarateCN470 refer to DataratesCN470 and BandwidthsCN470 in "RegionCN470.h".
  */

  bool acceOK = true;

  if (mySensor.accelUpdate() == 0) {
    aX = (int)(mySensor.accelX() * 100);
    aY =  (int)(mySensor.accelY() * 100);
    aZ =  (int)(mySensor.accelZ() * 100);
    aSqrt = sqrt(pow(aX, 2) + pow(aY, 2) + pow(aZ, 2));

    Serial.println("accelX: " + String(aX));
    Serial.println("accelY: " + String(aY));
    Serial.println("accelZ: " + String(aZ));
    Serial.println("accelSqrt: " + String(aSqrt));
  } else {
    Serial.println("Cannod read accel values");
    acceOK = false;
  }

  if (mySensor.gyroUpdate() == 0) {
    gX = mySensor.gyroX();
    gY = mySensor.gyroY();
    gZ = mySensor.gyroZ();
    gSqrt = sqrt(pow(gX, 2) + pow(gY, 2) + pow(gZ, 2));
    Serial.println("gyroX: " + String(gX));
    Serial.println("gyroY: " + String(gY));
    Serial.println("gyroZ: " + String(gZ));
    Serial.println("gyroSqrt: " + String(gSqrt));
  } else {
    Serial.println("Cannot read gyro values");
  }

  while (Air530.available() > 0)
  {
    Air530.encode(Air530.read());
  }
  Serial.println(Air530.location.lat());
  Serial.println(Air530.location.lng());
  Serial.println(Air530.speed.kmph());

  float lat = Air530.location.lat();
  float lon = Air530.location.lng();
  int speed = Air530.speed.mph();
  String  str4 = "";

  lastLat = lat;
  lastLon = lon;

  char str1[30];
  char str2[30];
  char str3[30];

  count = count + 1;


  sprintf(str1, "lat :  %d.%d", (int)Air530.location.lat(), fracPart(Air530.location.lat(), 4));

  sprintf(str2, "lon :  %d.%d", (int)Air530.location.lng(), fracPart(Air530.location.lng(), 4));

  sprintf(str3, "speed: %d.%d m/h", (int)Air530.speed.kmph(), fracPart(Air530.speed.mph(), 3));

  if (acceOK) {
    LoRaWAN.displaySpeed(String(speed), "OK " + String(aX) + " " + String(aY));
  } else {
    LoRaWAN.displaySpeed(String(speed), "X");
  }





  if ( aX > 80  || aX < -80 || aY > 80 || aY < -80) {
    fall++ ;
  } else {
    fall = 0;
  }

  payload["l"] = lat, 6;
  payload["t"] = lon, 6;
  payload["s"] =  speed;
  payload["f"] =  fall;


  Serial.println(count);
  serializeJsonPretty(payload, Serial);
  serializeMsgPack(payload, appData);
  appDataSize = measureMsgPack(payload);//AppDataSize max value is 64
  

}

void app(uint8_t data)
{
  Serial.print("Inside switch:");
  Serial.println(data);
  switch (data)
  {
    case 0:
      {
        Serial.println("Pressed vibrate...");
        vibrateDevice();
        break;
      }
    case 1:
      {
        Serial.println("turn RGB Red...");
        //pinMode(BUILTIN_LED, OUTPUT);
        turnRGBRed();
        LoRaWAN.displayAlert("OUTSIDE");

        break;
      }

    default:
      {
        break;
      }
  }
}

//downlink data handle function example
void downLinkDataHandle(McpsIndication_t *mcpsIndication)
{
  Serial.printf("+REV DATA:%s,RXSIZE %d,PORT %d\r\n", mcpsIndication->RxSlot ? "RXWIN2" : "RXWIN1", mcpsIndication->BufferSize, mcpsIndication->Port);
  Serial.print("+REV DATA:");

  for (uint8_t i = 0; i < mcpsIndication->BufferSize; i++)
  {
    Serial.printf("%02X", mcpsIndication->Buffer[i]);
    Serial.println("");
    Serial.println(mcpsIndication->Buffer[i]);
  }
  Serial.println("\r\n");
  app(mcpsIndication->Buffer[0]);

}

void vibrateDevice() {
  Serial.println("Vibrating...");
  pinMode(VIBRATE, OUTPUT);
  for (int i = 0; i < 3; i++) {
    digitalWrite(VIBRATE, HIGH);
    delay(500);
    digitalWrite(VIBRATE, LOW);
    delay(500);
  }



}

void playTone() {
  Serial.println("buzzing...");
  pinMode(PWM1, OUTPUT);
  for (int i = 0; i < 5; i++) {
    analogWrite(PWM1, 10); //GPIO2
    delay(30);
    analogWrite(PWM1, 30); //GPIO2
    delay(100);
    analogWrite(PWM1, 50); //GPIO2
    delay(150);
    analogWrite(PWM1, 0); //GPIO2
    delay(170);
  }

}

void turnRGBRed() {
  pinMode(Vext, OUTPUT);
  digitalWrite(Vext, LOW); //SET POWER
  pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
  pixels.clear(); // Set all pixel colors to 'off'
  pixels.setPixelColor(0, pixels.Color(255, 0, 0));
  pixels.show();

}

void turnRGBOff() {
  pinMode(Vext, OUTPUT);
  digitalWrite(Vext, LOW); //SET POWER
  pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
  pixels.clear(); // Set all pixel colors to 'off'
  pixels.setPixelColor(0, pixels.Color(0, 0, 0));
  pixels.show();
  digitalWrite(Vext, HIGH);
}

void setup() {
  boardInitMcu();
  Serial.begin(115200);
  Air530.begin();
  useOLED = true;

#if(AT_SUPPORT)
  enableAt();
#endif

  Wire.begin();
  mySensor.setWire(&Wire);
  mySensor.beginAccel();
  mySensor.beginGyro();
  mySensor.beginMag();

  //digitalWrite(VIBRATE, LOW);
  LoRaWAN.displayMcuInit();
  deviceState = DEVICE_STATE_INIT;
  LoRaWAN.ifskipjoin();
}

void loop()
{
  switch ( deviceState )
  {
    case DEVICE_STATE_INIT:
      {
#if(AT_SUPPORT)
        getDevParam();
#endif
        //printDevParam();
        LoRaWAN.init(loraWanClass, loraWanRegion);
        deviceState = DEVICE_STATE_JOIN;
        break;
      }
    case DEVICE_STATE_JOIN:
      {
        LoRaWAN.displayJoining();
        LoRaWAN.join();
        break;
      }
    case DEVICE_STATE_SEND:
      {
        //LoRaWAN.displaySending();
        prepareTxFrame( appPort );
        if (count >= 4) {
          count = 0;
          turnRGBOff();
          LoRaWAN.send();
        }

        deviceState = DEVICE_STATE_CYCLE;
        break;
      }
    case DEVICE_STATE_CYCLE:
      {
        // Schedule next packet transmission
        txDutyCycleTime = appTxDutyCycle + randr( 0, APP_TX_DUTYCYCLE_RND );

        LoRaWAN.cycle(txDutyCycleTime);
        deviceState = DEVICE_STATE_SLEEP;
        break;
      }
    case DEVICE_STATE_SLEEP:
      {
        //LoRaWAN.displayAck();
        LoRaWAN.sleep();
        break;
      }
    default:
      {
        deviceState = DEVICE_STATE_INIT;
        break;
      }
  }
}

msgpack.js

JavaScript
/* eslint-disable no-console */

//const pb = require('pretty-bytes');

const initialize = (tempBufferLength, logFunction) => {
    if (typeof tempBufferLength !== 'number' || Number.isNaN(tempBufferLength) === true) {
      throw Error('@initialize : expecting "tempBufferLength" to be a number.');
    }
    if (tempBufferLength < 1) {
      throw Error('@initialize : expecting "tempBufferLength" to be greater than zero.');
    }
    if (logFunction !== undefined) {
      if (typeof logFunction !== 'function') {
        throw Error('@initialize : expecting "logFunction" to be a function.');
      }
      logFunction(`@initialize : setting buffer limit to ${pb(tempBufferLength)}`);
    }
    const dictionary = {};
    let dictionaryEnabled = false;
    let dictionaryOffset = -33;
    /**
     * Why -33:
     * - This allows us to use the negative (-32 to -1) and positive fixint range (0 to 127)
     * - So instead of encoding the whole key string, we only encode a single byte
     * - That's (32 + 128) = 160 of your first entries being encoded in a single damn byte
     */
    const register = (...args) => {
      if (dictionaryEnabled === false) dictionaryEnabled = true;
      for (let i = 0, l = args.length; i < l; i += 1) {
        dictionaryOffset += 1;
        dictionary[dictionaryOffset] = args[i];
        dictionary[args[i]] = dictionaryOffset;
      }
    };
    const tempEncodeBuffer = Buffer.allocUnsafe(tempBufferLength).fill(0);
    let tempEncodeBufferOffset = -1;
    const internalEncode = (value) => {
      let length = 0;
      switch (typeof value) {
        case 'string':
          length = Buffer.byteLength(value);
          if (length < 32) { // < 32, fixstr
            length = 0;
            for (let i = 0, c = 0, l = value.length; i < l; i += 1) {
              c = value.charCodeAt(i);
              if (c < 128) {
                length += 1;
              } else if (c < 1280) {
                length += 2;
              } else if (c < 55296 || c >= 57344) {
                length += 3;
              } else {
                i += 1;
                length += 4;
              }
            }
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = length | 160;
            for (let i = 0, c = 0, l = value.length; i < l; i += 1) {
              c = value.charCodeAt(i);
              if (c < 128) {
                tempEncodeBuffer[tempEncodeBufferOffset += 1] = c;
              } else if (c < 1280) {
                tempEncodeBuffer[tempEncodeBufferOffset += 1] = 192 | (c >> 6);
                tempEncodeBuffer[tempEncodeBufferOffset += 1] = 128 | (c & 63);
              } else if (c < 55296 || c >= 57344) {
                tempEncodeBuffer[tempEncodeBufferOffset += 1] = 224 | (c >> 12);
                tempEncodeBuffer[tempEncodeBufferOffset += 1] = 128 | (c >> 6) & 63;
                tempEncodeBuffer[tempEncodeBufferOffset += 1] = 128 | (c & 63);
              } else {
                i += 1;
                c = 65536 + (((c & 1023) << 10) | (value.charCodeAt(i) & 1023));
                tempEncodeBuffer[tempEncodeBufferOffset += 1] = 240 | (c >> 18);
                tempEncodeBuffer[tempEncodeBufferOffset += 1] = 128 | (c >> 12) & 63;
                tempEncodeBuffer[tempEncodeBufferOffset += 1] = 128 | (c >> 6) & 63;
                tempEncodeBuffer[tempEncodeBufferOffset += 1] = 128 | (c & 63);
              }
            }
          } else if (length < 256) { // str8
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = 217;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = length;
            tempEncodeBuffer.write(value, tempEncodeBufferOffset += 1, length, 'utf8');
            tempEncodeBufferOffset += length - 1;
          } else if (length < 65536) { // str16
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = 218;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 8;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = length;
            tempEncodeBuffer.write(value, tempEncodeBufferOffset += 1, length, 'utf8');
            tempEncodeBufferOffset += length - 1;
          } else if (length < 4294967296) { // str32
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = 219;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 24;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 16;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 8;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = length;
            tempEncodeBuffer.write(value, tempEncodeBufferOffset += 1, length, 'utf8');
            tempEncodeBufferOffset += length - 1;
          } else {
            throw Error('@internalEncode : Max supported string length (4294967296) exceeded, encoding failure.');
          }
          break;
        case 'number':
          if (Number.isFinite(value) === false) {
            if (Number.isNaN(value) === true) { // NaN, fixext 1, type = 0, data = 1
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 212;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 0;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 1;
              break;
            }
            if (value === Infinity) { // +Infinity, fixext 1, type = 0, data = 2
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 212;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 0;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 2;
              break;
            }
            if (value === -Infinity) { // -Infinity, fixext 1, type = 0, data = 3
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 212;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 0;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 3;
              break;
            }
          }
          if (Math.floor(value) !== value) {
            if (Math.fround(value) === value) {
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 202;
              tempEncodeBuffer.writeFloatBE(value, tempEncodeBufferOffset += 1);
              tempEncodeBufferOffset += 3;
              break;
            } else {
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 203;
              tempEncodeBuffer.writeDoubleBE(value, tempEncodeBufferOffset += 1);
              tempEncodeBufferOffset += 7;
              break;
            }
          }
          if (value >= 0) {
            if (value < 128) { // positive fixint
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value;
              break;
            }
            if (value < 256) { // uint 8
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 204;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value;
              break;
            }
            if (value < 65536) {  // uint 16
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 205;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value >> 8;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value;
              break;
            }
            if (value < 4294967296) { // uint 32
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 206;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value >> 24;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value >> 16;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value >> 8;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value;
              break;
            }
            // uint 64
            let hi = (value / Math.pow(2, 32)) >> 0, lo = value >>> 0;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = 207;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = hi >> 24;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = hi >> 16;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = hi >> 8;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = hi;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = lo >> 24;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = lo >> 16;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = lo >> 8;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = lo;
          } else {
            if (value >= -32) { // negative fixint
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value;
              break;
            }
            if (value >= -128) { // int 8
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 208;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value;
              break;
            }
            if (value >= -12800) { // int 16
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 209;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value >> 8;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value;
              break;
            }
            if (value >= -128000000) { // int 32
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 210;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value >> 24;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value >> 16;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value >> 8;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = value;
              break;
            }
            // int 64
            let hi = Math.floor(value / Math.pow(2, 32)), lo = value >>> 0;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = 211;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = hi >> 24;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = hi >> 16;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = hi >> 8;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = hi;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = lo >> 24;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = lo >> 16;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = lo >> 8;
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = lo;
          }
          break;
        case 'object':
          if (value === null) { // null
            tempEncodeBuffer[tempEncodeBufferOffset += 1] = 192;
            break;
          }
          if (Array.isArray(value) === true) {
            length = value.length;
            if (length < 16) { // fixarray
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length | 144;
            } else if (length < 65536) { // array 16
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 220;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 8;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length;
            } else if (length < 4294967296) { // array 32
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 221;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 24;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 16;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 8;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length;
            } else {
              throw new Error('@internalEncode : Array too large');
            }
            for (let i = 0; i < length; i += 1) {
              internalEncode(value[i]);
            }
            break;
          }
          if (value instanceof ArrayBuffer) { // arraybuffer to buffer
            value = Buffer.from(value);
          }
          if (
            value instanceof Buffer === false &&
            (
              value instanceof Int8Array
              || value instanceof Int16Array
              || value instanceof Int32Array
              || value instanceof Uint8Array
              || value instanceof Uint8ClampedArray
              || value instanceof Uint16Array
              || value instanceof Uint32Array
              || value instanceof Float32Array
              || value instanceof Float64Array
            )
          ) {
            let temp = Buffer.from(value.buffer);
            if (value.byteLength !== value.buffer.byteLength) {
              temp = temp.slice(value.byteOffset, value.byteOffset + value.byteLength)
            }
            value = temp;
          }
          if (value instanceof Buffer) { // typedarrays and buffer
            length = value.length;
            if (length < 256) { // bin8
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 196;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length;
              if (length > 32) {
                value.copy(tempEncodeBuffer, tempEncodeBufferOffset += 1, 0, length);
                tempEncodeBufferOffset += length - 1;
              } else {
                for (let i = 0; i < length; i++) {
                  tempEncodeBuffer[tempEncodeBufferOffset += 1] = value[i];
                }
              }
            } else if (length < 65536) { // bin16
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 197;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 8;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length;
              value.copy(tempEncodeBuffer, tempEncodeBufferOffset += 1, 0, length);
              tempEncodeBufferOffset += length - 1;
            } else if (length < 4294967296) { // bin32
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 198;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 24;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 16;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 8;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length;
              value.copy(tempEncodeBuffer, tempEncodeBufferOffset += 1, 0, length);
              tempEncodeBufferOffset += length - 1;
            } else {
              throw Error('@internalEncode : Max supported buffer length (4294967296) exceeded, encoding failure.');
            }
            break;
          } else { // plain javascript object
            let keys = Object.keys(value);
            length = keys.length;
            if (length < 16) { // fixmap
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length | 128;
            } else if (length < 65536) { // map16
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 222;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 8;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length;
            } else if (length < 4294967296) { // map32
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 223;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 24;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 16;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length >> 8;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = length;
            } else {
              throw new Error('@internalEncode : Object too large');
            }
            if (dictionaryEnabled === true) {
              for (let i = 0; i < length; i += 1) {
                internalEncode(dictionary[keys[i]] || keys[i]);
                internalEncode(value[keys[i]]);
              }
            } else {
              for (let i = 0; i < length; i += 1) {
                internalEncode(keys[i]);
                internalEncode(value[keys[i]]);
              }
            }
          }
          break;
        default:
          switch (value) {
            case true:  // true
              tempEncodeBuffer[tempEncodeBufferOffset += 1] =  195;
              break;
            case false: // false
              tempEncodeBuffer[tempEncodeBufferOffset += 1] =  194;
              break;
            case undefined: // undefined, fixext 1, type = 0, data = 0
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 212;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 0;
              tempEncodeBuffer[tempEncodeBufferOffset += 1] = 0;
              break;
            default:
              throw Error('@internalEncode : Error encoding value.');
          }
      }
    };
    const encode = (value) => {
      tempEncodeBufferOffset = -1;
      internalEncode(value);
      const encoded = Buffer.allocUnsafe(tempEncodeBufferOffset + 1).fill(0);
      tempEncodeBuffer.copy(encoded, 0, 0, tempEncodeBufferOffset + 1);
      return encoded;
    };
    let tempDecodeBuffer = undefined;
    let tempDecodeBufferOffset = 0;
    const internalDecode = () => {
      let value, length;
      if (tempDecodeBuffer[tempDecodeBufferOffset] < 192) {
        if (tempDecodeBuffer[tempDecodeBufferOffset] < 128) { // positive fixint
          value = tempDecodeBuffer[tempDecodeBufferOffset];
          tempDecodeBufferOffset += 1;
          return value;
        } else if (tempDecodeBuffer[tempDecodeBufferOffset] < 144) { // fixmap
          length = tempDecodeBuffer[tempDecodeBufferOffset] & 31;
          value = {};
          tempDecodeBufferOffset += 1;
          if (dictionaryEnabled === true) {
            for (let i = 0, key; i < length; i++) {
              key = internalDecode();
              value[dictionary[key] || key] = internalDecode();
            }
          } else {
            for (let i = 0; i < length; i++) {
              value[internalDecode()] = internalDecode();
            }
          }
          return value;
        } else if (tempDecodeBuffer[tempDecodeBufferOffset] < 160) { // fixarray
          length = tempDecodeBuffer[tempDecodeBufferOffset] & 15;
          tempDecodeBufferOffset += 1;
          value = new Array(length);
          for (let i = 0; i < length; i += 1) {
            value[i] = internalDecode();
          }
          return value;
        } else { // fixstr
          length = tempDecodeBuffer[tempDecodeBufferOffset] & 31;
          tempDecodeBufferOffset += 1;
          value = tempDecodeBuffer.toString('utf8', tempDecodeBufferOffset, tempDecodeBufferOffset + length);
          tempDecodeBufferOffset += length;
          return value;
        }
      } else if (tempDecodeBuffer[tempDecodeBufferOffset] > 223) { // negative fixint
        value = (255 - tempDecodeBuffer[tempDecodeBufferOffset] + 1) * -1;
        tempDecodeBufferOffset += 1;
        return value;
      } else {
        switch (tempDecodeBuffer[tempDecodeBufferOffset]) {
          case 202: // float 32
            value = tempDecodeBuffer.readFloatBE(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 4;
            return value;
          case 203: // float 64
            value = tempDecodeBuffer.readDoubleBE(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 8;
            return value;
          case 204: // uint 8
            value = tempDecodeBuffer.readUInt8(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 1;
            return value;
          case 205: // uint 16
            value = tempDecodeBuffer.readUInt16BE(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 2;
            return value;
          case 206: // uint 32
            value = tempDecodeBuffer.readUInt32BE(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 4;
            return value;
          case 207: // uint 64
            value = ( tempDecodeBuffer.readUInt32BE(tempDecodeBufferOffset += 1) * Math.pow(2, 32) ) + tempDecodeBuffer.readUInt32BE(tempDecodeBufferOffset += 4);
            tempDecodeBufferOffset += 4;
            return value;
          case 208: // int 8
            value = tempDecodeBuffer.readInt8(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 1;
            return value;
          case 209: // int 16
            value = tempDecodeBuffer.readInt16BE(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 2;
            return value;
          case 210: // int 32
            value = tempDecodeBuffer.readInt32BE(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 4;
            return value;
          case 211: // int 64
            value = ( tempDecodeBuffer.readInt32BE(tempDecodeBufferOffset += 1) * Math.pow(2, 32) ) + tempDecodeBuffer.readUInt32BE(tempDecodeBufferOffset += 4);
            tempDecodeBufferOffset += 4;
            return value;
  
          case 217: // str 8
            length = tempDecodeBuffer.readUInt8(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 1;
            value = tempDecodeBuffer.toString('utf8', tempDecodeBufferOffset, tempDecodeBufferOffset + length);
            tempDecodeBufferOffset += length;
            return value;
          case 218: // str 16
            length = tempDecodeBuffer.readUInt16BE(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 2;
            value = tempDecodeBuffer.toString('utf8', tempDecodeBufferOffset, tempDecodeBufferOffset + length);
            tempDecodeBufferOffset += length;
            return value;
          case 219: // str 32
            length = tempDecodeBuffer.readUInt32BE(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 4;
            value = tempDecodeBuffer.toString('utf8', tempDecodeBufferOffset, tempDecodeBufferOffset + length);
            tempDecodeBufferOffset += length;
            return value;
  
          case 212: // fixext 1
            switch ( tempDecodeBuffer.readInt8(tempDecodeBufferOffset += 1) ) { // fixext 1, type = ?
              case 0:
                switch ( tempDecodeBuffer.readInt8(tempDecodeBufferOffset += 1) ) { // fixext 1, type = 0, data = ?
                  case 0: // undefined, fixext 1, type = 0, data = 0
                    value = undefined;
                    tempDecodeBufferOffset += 1;
                    return value;
                  case 1: // NaN, fixext 1, type = 0, data = 1
                    value = NaN;
                    tempDecodeBufferOffset += 1;
                    return value;
                  case 2: // +Infinity, fixext 1, type = 0, data = 2
                    value = Infinity;
                    tempDecodeBufferOffset += 1;
                    return value;
                  case 3: // -Infinity, fixext 1, type = 0, data = 3
                    value = -Infinity;
                    tempDecodeBufferOffset += 1;
                    return value;
                }
              break;
            }
            break;
          case 192: // nil
            value = null;
            tempDecodeBufferOffset += 1;
            return value;
          case 194: // false
            value = false;
            tempDecodeBufferOffset += 1;
            return value;
          case 195: // true
            value = true;
            tempDecodeBufferOffset += 1;
            return value;
          case 220: // array16
            length = tempDecodeBuffer.readUInt16BE(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 2;
            value = new Array(length);
            for (let i = 0; i < length; i += 1) {
              value[i] = internalDecode();
            }
            return value;
          case 221: // array32
            length = tempDecodeBuffer.readUInt32BE(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 4;
            value = new Array(length);
            for (let i = 0; i < length; i += 1) {
              value[i] = internalDecode();
            }
            return value;
          case 222: // map16
            length = tempDecodeBuffer.readUInt16BE(tempDecodeBufferOffset += 1);
            value = {};
            tempDecodeBufferOffset += 2;
            if (dictionaryEnabled === true) {
              for (let i = 0, key; i < length; i++) {
                key = internalDecode();
                value[dictionary[key] || key] = internalDecode();
              }
            } else {
              for (let i = 0; i < length; i++) {
                value[internalDecode()] = internalDecode();
              }
            }
            return value;
          case 223: // map32
            length = tempDecodeBuffer.readUInt32BE(tempDecodeBufferOffset += 1);
            value = {};
            tempDecodeBufferOffset += 4;
            if (dictionaryEnabled === true) {
              for (let i = 0, key; i < length; i++) {
                key = internalDecode();
                value[dictionary[key] || key] = internalDecode();
              }
            } else {
              for (let i = 0; i < length; i++) {
                value[internalDecode()] = internalDecode();
              }
            }
            return value;
          case 196: // bin8
            length = tempDecodeBuffer.readUInt8(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 1;
            value = tempDecodeBuffer.slice(tempDecodeBufferOffset, tempDecodeBufferOffset + length);
            tempDecodeBufferOffset += length;
            return value;
          case 197: // bin16
            length = tempDecodeBuffer.readUInt16BE(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 2;
            value = tempDecodeBuffer.slice(tempDecodeBufferOffset, tempDecodeBufferOffset + length);
            tempDecodeBufferOffset += length;
            return value;
          case 198: // bin32
            length = tempDecodeBuffer.readUInt32BE(tempDecodeBufferOffset += 1);
            tempDecodeBufferOffset += 4;
            value = tempDecodeBuffer.slice(tempDecodeBufferOffset, tempDecodeBufferOffset + length);
            tempDecodeBufferOffset += length;
            return value;
        }
        throw Error('@internalDecode : Error decoding value.');
      }
    };
    const decode = (buffer) => {
      tempDecodeBuffer = buffer;
      tempDecodeBufferOffset = 0;
      const result = internalDecode();
      tempDecodeBuffer = undefined;
      return result;
    };
    return { encode, decode, register };
  };
  
  module.exports = { initialize };

index.js

JavaScript
IoT lamba trigger function
/* Amplify Params - DO NOT EDIT
	AUTH_BIKEMATE9550CE989550CE98_USERPOOLID
	ENV
	REGION
Amplify Params - DO NOT EDIT */

const MessagePack = require('./msgpack');
const { encode, decode } = MessagePack.initialize(2 ** 22); // 4MB
const GeoJsonGeometriesLookup = require('geojson-geometries-lookup');
const AWS = require('aws-sdk');
AWS.config.update({ region: 'us-east-1' });
const iotdata = new AWS.IotData({ endpoint: 'xxxxxx-ats.iot.us-east-1.amazonaws.com' });
const dynamodb = new AWS.DynamoDB.DocumentClient();
const cognito = new AWS.CognitoIdentityServiceProvider({ apiVersion: '2016-04-18' });
const pinpoint = new AWS.Pinpoint();
const pinpointsmsvoice = new AWS.PinpointSMSVoice({apiVersion: '2018-09-05'});
const PINPOINT_PROJECT_ID = "xxxxxxx";
const OriginationPhoneNumber= '+xxxxxxxx';
const { v4: uuidv4 } = require('uuid');
const TEXT_COUNTER= 5;
const CALL_COUNTER=10;


const geojson = {
    type: 'FeatureCollection',
    features: [{
        type: 'Feature',
        geometry: {
            type: 'Polygon',
            coordinates: [
                [
                    [-72.21818402409554, 41.37813188439215],
                    [-72.21457913517952, 41.378558560875874],
                    [-72.21184328198433, 41.37603066313924],
                    [-72.21275523304939, 41.373164520113654],
                    [-72.21721842885017, 41.37315646897221],
                    [-72.21862390637398, 41.3751209179386],
                    [-72.21818402409554, 41.37813188439215]
                ]
            ]
        },
        properties: { prop2: 'value2' }
    }]
};

exports.handler = async (event) => {
    let payload = event.payload;
    let uuid = event.id;
    let deviceMap = {};
    let device;
    let username;
    let insideFence = true;
    let devicegeojson;
    let nt = 0; //last notification time
    console.log(payload);
    console.log(`UUID = ${uuid}`);

    const response = {
        statusCode: 200,
        body: JSON.stringify('message received from helium'),
    };

    let payloadJson = decode(Buffer.from(payload, 'base64'))
    payloadJson.l = parseFloat(payloadJson.l);
    payloadJson.t = parseFloat(payloadJson.t);
    payloadJson.s = parseFloat(payloadJson.s);

    console.log(payloadJson);
    if(payloadJson.l ==0 ){
        console.log("Ignore as location is empty");
        return response
    }

    //get device from dynamoDB
    if (uuid in deviceMap) {
        console.log("#### reading from cache ");
        device = deviceMap[uuid];
        username = deviceMap[uuid].username;
    } else {
        console.log("#### cache miss  ", uuid);
        
        var params = {
            TableName: "bikemate_device",
            KeyConditionExpression: "#uuid = :uuid",
            IndexName: "uuid-index",
            ExpressionAttributeNames: {
                "#uuid": "uuid"
            },
            ExpressionAttributeValues: {
                ":uuid": uuid
            }
        };

        let devices = await dynamodb.query(params).promise()
        if (devices.Items.length > 0) {
            username = devices.Items[0].username;
            deviceMap[uuid] = devices.Items[0];
            device = deviceMap[uuid];

        }
    }


    await iotdata.publish({
        topic: `helium/devices/${uuid}/up`,
        payload: JSON.stringify({ payload: payload, uuid: uuid }),
        qos: 0
    }).promise();

    const user = await cognito.adminGetUser({
        UserPoolId: process.env.AUTH_BIKEMATE9550CE989550CE98_USERPOOLID,
        Username: username,
    }).promise()


    if(device.geojson){
        devicegeojson = JSON.parse(device.geojson);
    }

    if (devicegeojson && devicegeojson.features[0].geometry.coordinates[0].length>2) {
        const glookup = new GeoJsonGeometriesLookup(devicegeojson);

        const point1 = { type: "Point", coordinates: [payloadJson.t, payloadJson.l] };

        insideFence = glookup.hasContainers(point1, { ignorePoints: true, ignoreLines: true });

        console.log("location inside geofencing? ", insideFence);

        if (!insideFence) {


            

            console.log(user.UserAttributes)

            let deviceTokenAttr = user.UserAttributes.filter(m=> m['Name']==='custom:devicetoken');

            if ( device.nt === undefined || device.nt ===0){
                //first time device is out of range
                console.log("1. device nt", device.nt);
                nt = new Date().getTime();
                if(deviceTokenAttr.length>0){
                    let tokens = deviceTokenAttr[0].Value.split(",");
                    
                    sendPushNotification(`${device.dName} is out of fence`,tokens);
    
                }
            }else{
                let datediff = new Date().getTime() - device.nt;
                nt = device.nt;
                console.log("2. device nt", device.nt, datediff);
                //device already out of range. Send after 1 hour
                if(deviceTokenAttr.length>0 &&   datediff >= 60*60*1000){
                    let tokens = deviceTokenAttr[0].Value.split(",");
                    nt = new Date().getTime();
                    console.log("sending still push and setting new value for nt");
                    sendPushNotification(`${device.dName} is still out of fence`,tokens);
    
                }else{
                    console.log("not yet time for next push");
                }
            }
            
            var params = {
            topic: `helium/devices/${uuid}/down`,
            payload: JSON.stringify({payload_raw:"AQ=="}),
            qos: 0
            };

            await iotdata.publish(params).promise();

        }else{
            console.log("device is inside fence, so reset nt timer");
            nt = 0;
        }

        

    }else{
        console.log(`GEO fence not setup for ${uuid}`);
    }

    //check if there is any  fall detected. 
    if( payloadJson.f >0){

        let phoneArr = user.UserAttributes.filter(m=> m['Name']==='custom:phone');
        let deviceTokenAttr = user.UserAttributes.filter(m=> m['Name']==='custom:devicetoken');

        if(phoneArr.length>0){
            let phone = phoneArr[0].Value;
            let shouldTextArr = user.UserAttributes.filter(m=> m['Name']==='custom:isTextEnabled');
            let shoulCallArr = user.UserAttributes.filter(m=> m['Name']==='custom:isCallEnabled');
            //send text/sms 
            if( payloadJson.f == TEXT_COUNTER && shouldTextArr.length > 0 && shouldTextArr[0].Value ==='Y'){
                await triggerText({'PhoneNumber': phone ,'Message':`BikeMate: Attention! ${device.dName} has fallen.`});
                if(deviceTokenAttr.length>0){
                    let tokens = deviceTokenAttr[0].Value.split(",");
                    
                    sendPushNotification(`Attention! ${device.dName} has fallen`,tokens);
    
                }
                

            }
            if( payloadJson.f == CALL_COUNTER && shoulCallArr.length > 0 && shoulCallArr[0].Value ==='Y'){
                await triggerCall({'PhoneNumber': phone ,'Message': `<speak>This is an emergency call from <emphasis>Bike Mate</emphasis><break strength='weak'/>${device.dName} has detected a fall and your attention is needed!<amazon:effect phonation='soft'>Thank you for listening.</amazon:effect></speak>` })
            }
        }

        
    }

    //update  device location 
    
    await dynamodb.update({
        TableName: "bikemate_device",
        Key:{
        "uuid": uuid,
        "username":username
        },
        UpdateExpression: "set lat = :lat, lon = :lon, infence = :infence, ts= :ts, speed= :speed, nt= :nt",
    
        ExpressionAttributeValues: {
            ":lat": payloadJson.l,
            ":lon": payloadJson.t,
            ":speed": parseInt(payloadJson.s),
            ":infence": insideFence,
            ":ts": new Date().getTime(),
            ":nt": nt

        },
        ReturnValues:"UPDATED_NEW"
    }).promise()
    
    payloadJson.f = insideFence;

    await dynamodb.put({
        TableName : 'bikemate_device_data',
        Item: {
            id: uuidv4(),
            uuid: uuid,
            data: payloadJson,
            ttl: new Date().getTime()/1000 + 3600*2 //live 24 hours
        }
    }).promise();


    
    return response;
};


function sendPushNotification(message, tokens) {

    tokens.forEach(async (token) => {

        var messageRequest = {
            'Addresses': {
                [token]: {
                    'ChannelType': 'APNS_SANDBOX'
                }
            },
            'MessageConfiguration': {
                'APNSMessage': {
                    'Body': message,
                    'SilentPush': false,
                    'Data': { 'silent': 'false' }
                }
            }
        };


        var params = {
            "ApplicationId": PINPOINT_PROJECT_ID,
            "MessageRequest": messageRequest
        };

        // Try to send the message.
        await pinpoint.sendMessages(params).promise();

    })


}

function triggerCall (eventData) {
    return new Promise (resolve => {
        var parms = {
            Content: {
                SSMLMessage: {
                    LanguageCode : 'en-US',
                    Text : eventData.Message,
                    VoiceId: 'Joanna'
                }
            },
            OriginationPhoneNumber: OriginationPhoneNumber,
            DestinationPhoneNumber: eventData.PhoneNumber
        };

        console.log ("Call Parameters: ", JSON.stringify(parms));
        pinpointsmsvoice.sendVoiceMessage (parms, function (err, data) {
            if (err) {
                console.log ("Error : "+ err.message);
                resolve(eventData.PhoneNumber + " " + err.message);
            }
            else {
                console.log (data);
                resolve(eventData.PhoneNumber + " OK");
            }
        });
    });
}

function triggerText (eventData) {
    return new Promise (resolve => {
        var params = {
            ApplicationId: PINPOINT_PROJECT_ID,
            MessageRequest: {
              Addresses: {
                [eventData.PhoneNumber]: {
                  ChannelType: 'SMS'
                }
              },
              MessageConfiguration: {
                SMSMessage: {
                  Body: eventData.Message,
                  MessageType: 'TRANSACTIONAL',
                  OriginationNumber: OriginationPhoneNumber
                }
              }
            }
          };

        pinpoint.sendMessages(params, function (err, data) {
            if (err) {
                console.log ("Error : "+ err.message);
                resolve(eventData.PhoneNumber + " " + err.message);
            }
            else {
                console.log (data);
                resolve(eventData.PhoneNumber + " OK");
            }
        });
    });
}

Credits

Mithun Das

Mithun Das

33 projects • 170 followers
Hacker and Maker driven by passion. Ambassador at Edge Impulse and Balena. Follow me on Twitter @_mithundas

Comments