Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
![]() |
| |||||
|
This is a demo project to demonstrate the strength of LPWAN connectivity. The goal was to build a low cost device which could run for a few months on a battery source and communicate over a Public loRaWAN network. For this purpose I used the Proximus LoRaWAN network available here in Belgium. The device itself is low cost and easy to build (minimum number of components). I was able to reduce power consumption to less than 30 microAmps in sleep mode and about 18 mA in transmitting mode, while the receiving base station from Proximus was more than 15 km away! This proves it's possible to build battery source powered device which lasts for months (28 following my calculations) using a Public LPWAN Network.
Select Your device
One's you have build the hardware, it's time to build your application on the AllThingsTalk Maker Cloud. Start with selecting your device

Configure your device
You can now configure your LoRaWAN Device. Enter a name, the DEVEUI, DEVADDR, NWSKEY and APPSKEY of your device. These are the keys necessary to register your device on the LoRaWAN network using ABP mode

Create your assets
After you configured your device, you can create your assets. We will need to create a Pushbutton asset (sensor type boolean) and battery asset(sensor, type integer)

Create Your APP dashboard
Next you can create your APP dashboard within the AllThingsTalk Maker cloud

Arduino Source Code
ArduinoThis is the Arduino Source code i used for the project. It makes use of the AllThingsTalk arduino LoRaWAN SDK (https://github.com/allthingstalk/att-arduino-lorawan-client/archive/master.zip).
The main functionality of the device is SLEEPING!. besides that, the RTC timer sends over the payload data each 24 hrs. The payload data counts in total 4 bytes including PB status and battery level. When the Pushbutton is pressed an interrupt wakes up the device and sends over the payload. I also included a reset button using the onboard push button of the device.
The main functionality of the device is SLEEPING!. besides that, the RTC timer sends over the payload data each 24 hrs. The payload data counts in total 4 bytes including PB status and battery level. When the Pushbutton is pressed an interrupt wakes up the device and sends over the payload. I also included a reset button using the onboard push button of the device.
/* This sketch samples the battery level each 24 hrs and triggers on a button push and sends it over using the LoRaWAN protocol
* The sketch has been optimized for minimum battery usages.
*
*/
/* payload structure
* (pos, bitlenght)
* buttonState (0,2)
* bat% (2,2)
*/
#include <RTCZero.h>
#include <Wire.h>
#include <ATT_IOT_LoRaWAN.h>
#include <MicrochipLoRaModem.h>
#include "keys.h"
#include <math.h>
#include "InstrumentationPacket.h"
const int B=4275; // B value of the thermistor
const int R0 = 100000; // R0 = 100k
#define SERIAL_BAUD 57600
#define enablePin 11
#define pushButton 1
#define ADC_AREF 3.3f
#define BATVOLT_R1 2.0f
#define BATVOLT_R2 2.0f
#define BATVOLT_PIN BAT_VOLT
#define WAKEUP_EVERY_SEC 59 // Seconds part of the clock that wakes up the device
#define WAKEUP_EVERY_MIN 59 // Minutes part of the clock that wakes up the device
#define WAKEUP_EVERY_HR 23 // Hours part of the clock that wakes up the device
RTCZero rtc;
volatile bool rtc_flag = false;
volatile bool pb_Push = false;
volatile bool pb_Reset = false;
MicrochipLoRaModem Modem(&Serial1, &SerialUSB);
ATTDevice Device(&Modem, &SerialUSB, false, 7000); // Min Time between 2 consecutive messages set @ 7 seconds
// Payload data structure
struct payload
{
bool value1;
short value2;
};
payload data;
// Ledcolors
enum LedColor {
NONE = 0,
RED,
GREEN,
BLUE,
YELLOW,
MAGENTA,
CYAN,
WHITE
};
// Interrupt routine
void alarmMatch()
{
rtc_flag = true;
}
uint16_t getBatteryVoltage()
{
uint16_t voltage = (uint16_t)((ADC_AREF / 1.023) * (BATVOLT_R1 + BATVOLT_R2) / BATVOLT_R2 * (float)analogRead(BATVOLT_PIN));
int val = voltage*100/4095;
if (val > 100) val = 100;
return (val);
}
/**
* Turns the led on according to the given color. Makes no assumptions about the status of the pins
* i.e. it sets them every time,
*/
void setLedColor(LedColor color)
{
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
digitalWrite(LED_RED, HIGH);
digitalWrite(LED_GREEN, HIGH);
digitalWrite(LED_BLUE, HIGH);
switch (color)
{
case NONE:
break;
case RED:
digitalWrite(LED_RED, LOW);
break;
case GREEN:
digitalWrite(LED_GREEN, LOW);
break;
case BLUE:
digitalWrite(LED_BLUE, LOW);
break;
case YELLOW:
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_RED, LOW);
break;
case MAGENTA:
digitalWrite(LED_BLUE, LOW);
digitalWrite(LED_RED, LOW);
break;
case CYAN:
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_BLUE, LOW);
break;
case WHITE:
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_RED, LOW);
digitalWrite(LED_BLUE, LOW);
break;
default:
break;
}
}
void buttonPushedHandler() {
pb_Push = true;
pb_Reset = false;
}
void buttonResetHandler() {
pb_Reset = true;
pb_Push = false;
}
void setup()
{
pinMode(ENABLE_PIN_IO, OUTPUT);
digitalWrite(ENABLE_PIN_IO, HIGH);
delay(100);
pinMode(enablePin, OUTPUT);
digitalWrite(enablePin, LOW); // If you want to use the pins: 2/3, 6/7 and 8/9 you first have to set pin D11 high.
// Configure the button as an input and enable the internal pull-up resistor (reset)
pinMode(BUTTON, INPUT_PULLUP);
attachInterrupt(BUTTON, buttonResetHandler, LOW);
// Configure PIN d2 as an input and enable the internal pull-up resistor
pinMode(pushButton, INPUT_PULLUP);
attachInterrupt(pushButton, buttonPushedHandler, LOW);
SerialUSB.begin(SERIAL_BAUD); // set baud rate of the default serial debug connection
while((!SerialUSB) && (millis()) < 10000){} //wait until serial bus is available, so we get the correct logging on screen. If no serial, then blocks for 2 seconds before run
setLedColor(BLUE); // BLUE led indicates the device is booting
delay(5000);
setLedColor(NONE);
Serial1.begin(Modem.getDefaultBaudRate()); // init the baud rate of the serial connection so that it's ok for the modem
while((!Serial1) && (millis()) < 30000){} //wait until serial bus is available, so we get the correct logging on screen. If no serial, then blocks for 2 seconds before run
while(!Device.Connect(DEV_ADDR, APPSKEY, NWKSKEY))
Serial.println("retrying..."); // initialize connection with the AllThingsTalk Developer Cloud
SerialUSB.println("Ready to send data");
/* turn of GPS
pinMode(GPS_ENABLE, OUTPUT);
digitalWrite(GPS_ENABLE, LOW);
*/
/*
// disable accelerometer, power-down mode
lsm303.writeReg(LSM303::CTRL1, 0);
// zero CTRL5 (including turn off TEMP sensor)
lsm303.writeReg(LSM303::CTRL5, 0);
// disable magnetometer, power-down mode
lsm303.writeReg(LSM303::CTRL7, 0b00000010);
*/
// turn of USB
// USBDevice.detach();
Modem.Sleep();
rtc.begin();
rtc.setEpoch(0); // This sets it to 2000-01-01
rtc.setAlarmSeconds(WAKEUP_EVERY_SEC); // Schedule the wakeup interrupt
rtc.setAlarmMinutes(WAKEUP_EVERY_MIN);
rtc.setAlarmHours(WAKEUP_EVERY_HR);
rtc.enableAlarm(rtc.MATCH_HHMMSS); // MATCH_HHMMSS
rtc.attachInterrupt(alarmMatch); // Attach handler so that we can set the battery flag when the time has passed.
Sensorsampling();
// payload transmittion
sendData();
// deepsleep
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__WFI();
delay(100);
}
void Sensorsampling()
{
if (pb_Push) {
data.value1 = true;
}
else {
data.value1 = false;
}
SerialUSB.print("Alarmbutton Pressed: "); SerialUSB.println(pb_Push);
short val = getBatteryVoltage();
SerialUSB.print("Battery Voltage (%): "); SerialUSB.println(val);
data.value2 = val;
}
void sendData()
{
Modem.WakeUp();
setLedColor(BLUE);
SerialUSB.print("#bytes in payload: "); SerialUSB.println(sizeof(data));
Device.Send(&data, sizeof(data), true);
//Device.Send(&data, sizeof(data), false); // without ACK!
Device.ProcessQueue();
while(Device.ProcessQueuePopFailed() > 0) {
SerialUSB.print("QueueCount: "); SerialUSB.println(Device.QueueCount());
delay(10000);
}
setLedColor(NONE);
Modem.Sleep();
delay(500);
}
void loop()
{
if(rtc_flag) {
SerialUSB.print("WAKING UP! ");
rtc.disableAlarm();
Sensorsampling();
// payload transmittion
//sendData();
rtc_flag = false;
rtc.setEpoch(0); // This sets it to 2000-01-01
rtc.enableAlarm(rtc.MATCH_HHMMSS);
}
if (pb_Push) {
setLedColor(RED);
delay(2000);
setLedColor(NONE);
Sensorsampling();
// payload transmittion
sendData();
pb_Push = false;
}
if (pb_Reset) {
setLedColor(MAGENTA);
delay(2000);
setLedColor(NONE);
Sensorsampling();
// payload transmittion
sendData();
pb_Reset = false;
}
rtc.standbyMode(); // Sleep until next alarm match
}
Comments