Skip to main content

WisBlock IoT Education Kit - Light & Color Quick Start Guide

Prerequisite

What Do You Need?

The WisBlock IoT Education Kit - Light & Color comes with RAK19007 and RAK19001 WisBlock Base boards, two RAK4631 WisBlock Core modules, and set of sensor modules to explore with. Before going through each and every step on using the WisBlock IoT Education Kit - Light & Color, make sure to prepare the necessary items listed below:

Hardware

Software

Arduino

Product Configuration

Hardware Setup & Sample Applications

WisBlock IoT Education Kit - Light & Color comprises of UV and light sensors, and RGB LED modules that can be used with RAK19007 and RAK19001 WisBlock Base boards which you can choose from. This kit will help you learn how to measure light and colors and how to create different light sources. You can choose from these devices for your desired applications.

  • UV Index & UV Intensity Meter - RAK4631 + RAK12019 + RAK12010 + RAK14003
Figure 1628: RAK4631 + RAK12019 + RAK12010 + RAK14003
  • RGB LED Color Changer - RAK4631 + RAK12021 + RAK14001
Figure 1629: RAK4631 + RAK12021 + RAK14001

Assembly and Functionality Tests of WisBlock Light and Color Modules

The following WisBlock Light and Color modules listed below are used in this kit. To check for their asssemblies and functionalities, please refer to the links below for further information:

Software Configuration and Examples

LoRaWAN Applications for WisBlock IoT Education Kit - Light & Color using TTN and Qubitro

UV Index & UV Intensity Monitoring LoRaWAN Application

The UV Index & UV Intensity Monitoring LoRaWAN Application is used to monitor the UV index, UV intensity and light intensity from the sun exposure. It uses RAK12019 which is an ambient light sensor and a ultraviolet sensor which is based on LTR-390UV-01 from Lite-On. Also, it uses RAK12010 which is an ambient light sensor which is based from VEML7700 chip from Vishay Semiconductors. And finally the RAK14003 LED bar module which visualizes the UV Index level measured from the RAK12019 module.

UV Index is the measure of the level of UV radiation from the sun exposure. It ranges from zero upward. The higher the UV Index level, the greater potential for damage to the skin and eye. The UV Index & UV Intensity Monitoring device will help alert people about the need to use sun protection when going outside. Below is the UV Index Scale which can help users monitor the level of the UV Index during daytime. For further details about UV index levels and their impacts, you can refer from this link.

Figure 1630: UV Index Scale
Figure 1631: UV Index & UV Intensity Monitoring device
UV Index & UV Intensity Monitoring - TTN Registration Section and Device Registration
  1. If you already have an existing TTN account, you may proceed to the next steps. If you haven't created any TTN account, please refer to this link to create one.

  2. Once done with the TTN account creation, you may now proceed with the device registration. Please refer to this guide for your reference. After creating the application and adding the device in TTN, you can proceed on the LoRaWAN Code uploading steps.

LoRaWAN Code for UV Index & UV Intensity Monitoring
  1. If you already have Arduino IDE installed to your laptop or PC and added RAK4631 board into it, you may proceed to the next step. If you haven't yet installed the Arduino IDE, please refer to this link for the steps.

  2. After the installation, you can now proceed programming your UV Index & UV Intensity Monitoring device. Just copy the code below for UV Index & UV Intensity Monitoring LoRaWAN Application and paste it into the Arduino IDE.

NOTE

The example code uses SX126x-Arduino library which needs to be added to successfully compile the LoRaWAN code.

Click to view the example
#include <Arduino.h>
#include <LoRaWan-RAK4630.h> //http://librarymanager/All#SX126x
#include <SPI.h>
#include <Wire.h>

#include "UVlight_LTR390.h" // http://librarymanager/All#RAK12019_LTR390
UVlight_LTR390 ltr = UVlight_LTR390();

#include "Light_VEML7700.h" // http://librarymanager/All#Light_veml7700
Light_VEML7700 VMEL = Light_VEML7700();

uint16_t uvi;
uint32_t uvs;
uint32_t lux;
uint32_t white;
uint32_t raw_als;

#include <Adafruit_MCP23X17.h> //http://librarymanager/All#Adafruit_MCP23017
Adafruit_MCP23X17 mcp;

// Added definitions for the functionality of RAK14003
const uint8_t LED_0 = 0; // PA0 (17) of MCP23017
const uint8_t LED_1 = 1; // PA1 (18) of MCP23017
const uint8_t LED_2 = 2; // PA2 (19) of MCP23017
const uint8_t LED_3 = 3; // PA3 (20) of MCP23017
const uint8_t LED_4 = 4; // PA4 (21) of MCP23017
const uint8_t LED_5 = 5; // PA5 (22) of MCP23017
const uint8_t LED_6 = 6; // PA6 (23) of MCP23017
const uint8_t LED_7 = 7; // PA7 (24) of MCP23017
const uint8_t LED_8 = 8; // PB0 (25) of MCP23017
const uint8_t LED_9 = 9; // PB1 (26) of MCP23017

#ifdef RAK4630
#define BOARD "RAK4631 "
#define RAK4631_BOARD true
#else
#define RAK4631_BOARD false
#endif

bool doOTAA = true; // OTAA is used by default.
#define SCHED_MAX_EVENT_DATA_SIZE APP_TIMER_SCHED_EVENT_DATA_SIZE /**< Maximum size of scheduler events. */
#define SCHED_QUEUE_SIZE 60 /**< Maximum number of events in the scheduler queue. */
#define LORAWAN_DATERATE DR_0 /*LoRaMac datarates definition, from DR_0 to DR_5*/
#define LORAWAN_TX_POWER TX_POWER_5 /*LoRaMac tx power definition, from TX_POWER_0 to TX_POWER_15*/
#define JOINREQ_NBTRIALS 3 /**< Number of trials for the join request. */
DeviceClass_t g_CurrentClass = CLASS_A; /* class definition*/
LoRaMacRegion_t g_CurrentRegion = LORAMAC_REGION_US915; /* Region:EU868*/
lmh_confirm g_CurrentConfirm = LMH_CONFIRMED_MSG; /* confirm/unconfirm packet definition*/
uint8_t gAppPort = LORAWAN_APP_PORT; /* data port*/

/**@brief Structure containing LoRaWan parameters, needed for lmh_init()
*/
static lmh_param_t g_lora_param_init = { LORAWAN_ADR_ON, LORAWAN_DATERATE, LORAWAN_PUBLIC_NETWORK, JOINREQ_NBTRIALS, LORAWAN_TX_POWER, LORAWAN_DUTYCYCLE_OFF };

// Forward declaration
static void lorawan_has_joined_handler(void);
static void lorawan_join_failed_handler(void);
static void lorawan_rx_handler(lmh_app_data_t *app_data);
static void lorawan_confirm_class_handler(DeviceClass_t Class);
static void send_lora_frame(void);

/**@brief Structure containing LoRaWan callback functions, needed for lmh_init()
*/
static lmh_callback_t g_lora_callbacks = { BoardGetBatteryLevel, BoardGetUniqueId, BoardGetRandomSeed,
lorawan_rx_handler, lorawan_has_joined_handler, lorawan_confirm_class_handler, lorawan_join_failed_handler };
//OTAA keys !!!! KEYS ARE MSB !!!!
uint8_t nodeDeviceEUI[8] = { 0xAC, 0x1F, 0x09, 0xFF, 0xFE, 0x06, 0xD3, 0xE9 };
uint8_t nodeAppEUI[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t nodeAppKey[16] = { 0xE9, 0x0D, 0x28, 0x1D, 0x92, 0x3D, 0xE4, 0x9F, 0xD9, 0xF7, 0xE0, 0x5F, 0x39, 0x10, 0x55, 0x71 };

// ABP keys
uint32_t nodeDevAddr = 0x260116F8;
uint8_t nodeNwsKey[16] = { 0x7E, 0xAC, 0xE2, 0x55, 0xB8, 0xA5, 0xE2, 0x69, 0x91, 0x51, 0x96, 0x06, 0x47, 0x56, 0x9D, 0x23 };
uint8_t nodeAppsKey[16] = { 0xFB, 0xAC, 0xB6, 0x47, 0xF3, 0x58, 0x45, 0xC7, 0x50, 0x7D, 0xBF, 0x16, 0x8B, 0xA8, 0xC1, 0x7C };

// Private definition
#define LORAWAN_APP_DATA_BUFF_SIZE 64 /**< buffer size of the data to be transmitted. */
static uint8_t m_lora_app_data_buffer[LORAWAN_APP_DATA_BUFF_SIZE]; //< Lora user application data buffer.
static lmh_app_data_t m_lora_app_data = { m_lora_app_data_buffer, 0, 0, 0, 0 }; //< Lora user application data structure.

static uint32_t count = 0;
static uint32_t count_fail = 0;

void setup() {

// Initialize Serial for debug output
pinMode(LED_BLUE, OUTPUT);
digitalWrite(LED_BLUE, HIGH);

// Sensor Power Switch
pinMode(WB_IO2, OUTPUT);
digitalWrite(WB_IO2, HIGH);

// Reset RAK14003 module
pinMode(WB_IO4, OUTPUT);
digitalWrite(WB_IO4, HIGH);
delay(10);
digitalWrite(WB_IO4, LOW);
delay(10);
digitalWrite(WB_IO4, HIGH);
delay(10);

// Initialize LoRa chip.
lora_rak4630_init();

// Initialize Serial for debug output
time_t timeout = millis();
Serial.begin(115200);
while (!Serial) {
if ((millis() - timeout) < 5000) {
delay(100);
} else {
break;
}
}
Serial.println("=====================================");
Serial.println("Welcome to RAK4630 LoRaWan!!!");
if (doOTAA) {
Serial.println("Type: OTAA");
} else {
Serial.println("Type: ABP");
}

switch (g_CurrentRegion) {
case LORAMAC_REGION_AS923:
Serial.println("Region: AS923");
break;
case LORAMAC_REGION_AU915:
Serial.println("Region: AU915");
break;
case LORAMAC_REGION_CN470:
Serial.println("Region: CN470");
break;
case LORAMAC_REGION_EU433:
Serial.println("Region: EU433");
break;
case LORAMAC_REGION_IN865:
Serial.println("Region: IN865");
break;
case LORAMAC_REGION_EU868:
Serial.println("Region: EU868");
break;
case LORAMAC_REGION_KR920:
Serial.println("Region: KR920");
break;
case LORAMAC_REGION_US915:
Serial.println("Region: US915");
break;
}
Serial.println("=====================================");

// Setup the EUIs and Keys
if (doOTAA) {
lmh_setDevEui(nodeDeviceEUI);
lmh_setAppEui(nodeAppEUI);
lmh_setAppKey(nodeAppKey);
} else {
lmh_setNwkSKey(nodeNwsKey);
lmh_setAppSKey(nodeAppsKey);
lmh_setDevAddr(nodeDevAddr);
}

uint32_t err_code;
// Initialize LoRaWan
err_code = lmh_init(&g_lora_callbacks, g_lora_param_init, doOTAA, g_CurrentClass, g_CurrentRegion);
if (err_code != 0) {
Serial.printf("lmh_init failed - %d\n", err_code);
return;
}

// Start Join procedure
lmh_join();

Serial.println("================================");
Serial.println("RAK12019 + RAK12010 LoRaWAN Code");
Serial.println("================================");

if (!VMEL.begin())
{
Serial.println("Sensor not found");
while (1);
}

VMEL.setGain(VEML7700_GAIN_1);
VMEL.setIntegrationTime(VEML7700_IT_800MS);

Serial.print(F("Gain: "));
switch (VMEL.getGain())
{
case VEML7700_GAIN_1: Serial.println("1"); break;
case VEML7700_GAIN_2: Serial.println("2"); break;
case VEML7700_GAIN_1_4: Serial.println("1/4"); break;
case VEML7700_GAIN_1_8: Serial.println("1/8"); break;
}

Serial.print(F("Integration Time (ms): "));
switch (VMEL.getIntegrationTime())
{
case VEML7700_IT_25MS: Serial.println("25"); break;
case VEML7700_IT_50MS: Serial.println("50"); break;
case VEML7700_IT_100MS: Serial.println("100"); break;
case VEML7700_IT_200MS: Serial.println("200"); break;
case VEML7700_IT_400MS: Serial.println("400"); break;
case VEML7700_IT_800MS: Serial.println("800"); break;
}

Serial.println("RAK12019 test");
Wire.begin();
if (!ltr.init())
{
Serial.println("Couldn't find LTR sensor!");
while (1)
delay(10);
}
Serial.println("Found LTR390 sensor!");

//set to LTR390_MODE_UVS,get ultraviolet light data.
ltr.setMode(LTR390_MODE_UVS); // UVS Mode

Serial.println("In UVS mode");

ltr.setGain(LTR390_GAIN_3);
Serial.print("Gain : ");
switch (ltr.getGain())
{
case LTR390_GAIN_1:
Serial.println(1);
break;
case LTR390_GAIN_3:
Serial.println(3);
break;
case LTR390_GAIN_6:
Serial.println(6);
break;
case LTR390_GAIN_9:
Serial.println(9);
break;
case LTR390_GAIN_18:
Serial.println(18);
break;
default:
Serial.println("Failed to set gain");
break;
}
ltr.setResolution(LTR390_RESOLUTION_16BIT);
Serial.print("Integration Time (ms): ");
switch (ltr.getResolution())
{
case LTR390_RESOLUTION_13BIT:
Serial.println(13);
break;
case LTR390_RESOLUTION_16BIT:
Serial.println(16);
break;
case LTR390_RESOLUTION_17BIT:
Serial.println(17);
break;
case LTR390_RESOLUTION_18BIT:
Serial.println(18);
break;
case LTR390_RESOLUTION_19BIT:
Serial.println(19);
break;
case LTR390_RESOLUTION_20BIT:
Serial.println(20);
break;
default:
Serial.println("Failed to set Integration Time");
break;
}

ltr.setThresholds(100, 1000); //Set the interrupt output threshold range for lower and upper.

ltr.configInterrupt(true, LTR390_MODE_UVS);

// For RAK14003
mcp.begin_I2C(0x24);

// Added for the functionality of RAK14003

Wire.begin(); // initialize I2C serial bus

// Configuration of MCP23017 I/O ports
mcp.pinMode(LED_0, OUTPUT); // Red LED; Top most
mcp.pinMode(LED_1, OUTPUT); // Red LED
mcp.pinMode(LED_2, OUTPUT); // Orange LED
mcp.pinMode(LED_3, OUTPUT); // Orange LED
mcp.pinMode(LED_4, OUTPUT); // Orange LED
mcp.pinMode(LED_5, OUTPUT); // Green LED
mcp.pinMode(LED_6, OUTPUT); // Green LED
mcp.pinMode(LED_7, OUTPUT); // Green LED
mcp.pinMode(LED_8, OUTPUT); // Green LED
mcp.pinMode(LED_9, OUTPUT); // Green LED; Bottom

// Initially Turn OFF all LEDs
mcp.digitalWrite(LED_0, HIGH);
mcp.digitalWrite(LED_1, HIGH);
mcp.digitalWrite(LED_2, HIGH);
mcp.digitalWrite(LED_3, HIGH);
mcp.digitalWrite(LED_4, HIGH);
mcp.digitalWrite(LED_5, HIGH);
mcp.digitalWrite(LED_6, HIGH);
mcp.digitalWrite(LED_7, HIGH);
mcp.digitalWrite(LED_8, HIGH);
mcp.digitalWrite(LED_9, HIGH);

}

void loop() {
// Put your application tasks here, like reading of sensors,
// Controlling actuators and/or other functions.

// Trial # 1
// Data from RAK12019
if (ltr.newDataAvailable())
{
uvi = ltr.getUVI();
uvs = ltr.readUVS();
}

// Data from RAK12010
lux = VMEL.readLux();
white = VMEL.readWhite();
raw_als = VMEL.readALS();

Serial.println("Sending frame now...");
send_lora_frame();

// Showing only UV Index (uvi), UV Intensity (uvs) and Lux (lx)

// From RAK12019 module
Serial.print("UVI: ");
Serial.println(uvi); // Uvi data from RAK12019
Serial.print("UVS: ");
Serial.println(uvs); // Uvs data from RAK12019

// From RAK12010
Serial.print("Lux: ");
Serial.print(lux);
Serial.println(" lx ");
Serial.print("White: ");
Serial.println(white);
Serial.print("Raw ALS: ");
Serial.println(raw_als);

// Result of uvi to be displayed via RAK14003 module
if (uvi <= 0.4)
{
mcp.digitalWrite(LED_0, HIGH);
mcp.digitalWrite(LED_1, HIGH);
mcp.digitalWrite(LED_2, HIGH);
mcp.digitalWrite(LED_3, HIGH);
mcp.digitalWrite(LED_4, HIGH);
mcp.digitalWrite(LED_5, HIGH);
mcp.digitalWrite(LED_6, HIGH);
mcp.digitalWrite(LED_7, HIGH);
mcp.digitalWrite(LED_8, HIGH);
mcp.digitalWrite(LED_9, HIGH);
}

else if (uvi > 0.5 && uvi <= 1)
{
mcp.digitalWrite(LED_0, HIGH);
mcp.digitalWrite(LED_1, HIGH);
mcp.digitalWrite(LED_2, HIGH);
mcp.digitalWrite(LED_3, HIGH);
mcp.digitalWrite(LED_4, HIGH);
mcp.digitalWrite(LED_5, HIGH);
mcp.digitalWrite(LED_6, HIGH);
mcp.digitalWrite(LED_7, HIGH);
mcp.digitalWrite(LED_8, HIGH);
mcp.digitalWrite(LED_9, LOW);
}

else if (uvi > 1.5 && uvi <= 2)
{
mcp.digitalWrite(LED_0, HIGH);
mcp.digitalWrite(LED_1, HIGH);
mcp.digitalWrite(LED_2, HIGH);
mcp.digitalWrite(LED_3, HIGH);
mcp.digitalWrite(LED_4, HIGH);
mcp.digitalWrite(LED_5, HIGH);
mcp.digitalWrite(LED_6, HIGH);
mcp.digitalWrite(LED_7, HIGH);
mcp.digitalWrite(LED_8, LOW);
mcp.digitalWrite(LED_9, LOW);
}

else if (uvi > 2.5 && uvi <= 3)
{
mcp.digitalWrite(LED_0, HIGH);
mcp.digitalWrite(LED_1, HIGH);
mcp.digitalWrite(LED_2, HIGH);
mcp.digitalWrite(LED_3, HIGH);
mcp.digitalWrite(LED_4, HIGH);
mcp.digitalWrite(LED_5, HIGH);
mcp.digitalWrite(LED_6, HIGH);
mcp.digitalWrite(LED_7, LOW);
mcp.digitalWrite(LED_8, LOW);
mcp.digitalWrite(LED_9, LOW);
}

else if (uvi > 3.5 && uvi <= 4)
{
mcp.digitalWrite(LED_0, HIGH);
mcp.digitalWrite(LED_1, HIGH);
mcp.digitalWrite(LED_2, HIGH);
mcp.digitalWrite(LED_3, HIGH);
mcp.digitalWrite(LED_4, HIGH);
mcp.digitalWrite(LED_5, HIGH);
mcp.digitalWrite(LED_6, LOW);
mcp.digitalWrite(LED_7, LOW);
mcp.digitalWrite(LED_8, LOW);
mcp.digitalWrite(LED_9, LOW);
}

else if (uvi > 4.5 && uvi <= 5)
{
mcp.digitalWrite(LED_0, HIGH);
mcp.digitalWrite(LED_1, HIGH);
mcp.digitalWrite(LED_2, HIGH);
mcp.digitalWrite(LED_3, HIGH);
mcp.digitalWrite(LED_4, HIGH);
mcp.digitalWrite(LED_5, LOW);
mcp.digitalWrite(LED_6, LOW);
mcp.digitalWrite(LED_7, LOW);
mcp.digitalWrite(LED_8, LOW);
mcp.digitalWrite(LED_9, LOW);
}

else if (uvi > 5.5 && uvi <= 6)
{
mcp.digitalWrite(LED_0, HIGH);
mcp.digitalWrite(LED_1, HIGH);
mcp.digitalWrite(LED_2, HIGH);
mcp.digitalWrite(LED_3, HIGH);
mcp.digitalWrite(LED_4, LOW);
mcp.digitalWrite(LED_5, LOW);
mcp.digitalWrite(LED_6, LOW);
mcp.digitalWrite(LED_7, LOW);
mcp.digitalWrite(LED_8, LOW);
mcp.digitalWrite(LED_9, LOW);
}

else if (uvi > 6.5 && uvi <= 7)
{
mcp.digitalWrite(LED_0, HIGH);
mcp.digitalWrite(LED_1, HIGH);
mcp.digitalWrite(LED_2, HIGH);
mcp.digitalWrite(LED_3, LOW);
mcp.digitalWrite(LED_4, LOW);
mcp.digitalWrite(LED_5, LOW);
mcp.digitalWrite(LED_6, LOW);
mcp.digitalWrite(LED_7, LOW);
mcp.digitalWrite(LED_8, LOW);
mcp.digitalWrite(LED_9, LOW);
}

else if (uvi > 7.5 && uvi <= 8)
{
mcp.digitalWrite(LED_0, HIGH);
mcp.digitalWrite(LED_1, HIGH);
mcp.digitalWrite(LED_2, LOW);
mcp.digitalWrite(LED_3, LOW);
mcp.digitalWrite(LED_4, LOW);
mcp.digitalWrite(LED_5, LOW);
mcp.digitalWrite(LED_6, LOW);
mcp.digitalWrite(LED_7, LOW);
mcp.digitalWrite(LED_8, LOW);
mcp.digitalWrite(LED_9, LOW);
}

else if (uvi > 8.5 && uvi <= 9.5)
{
mcp.digitalWrite(LED_0, HIGH);
mcp.digitalWrite(LED_1, LOW);
mcp.digitalWrite(LED_2, LOW);
mcp.digitalWrite(LED_3, LOW);
mcp.digitalWrite(LED_4, LOW);
mcp.digitalWrite(LED_5, LOW);
mcp.digitalWrite(LED_6, LOW);
mcp.digitalWrite(LED_7, LOW);
mcp.digitalWrite(LED_8, LOW);
mcp.digitalWrite(LED_9, LOW);
}

else if (uvi >= 10)
{
mcp.digitalWrite(LED_0, LOW);
mcp.digitalWrite(LED_1, LOW);
mcp.digitalWrite(LED_2, LOW);
mcp.digitalWrite(LED_3, LOW);
mcp.digitalWrite(LED_4, LOW);
mcp.digitalWrite(LED_5, LOW);
mcp.digitalWrite(LED_6, LOW);
mcp.digitalWrite(LED_7, LOW);
mcp.digitalWrite(LED_8, LOW);
mcp.digitalWrite(LED_9, LOW);
}

delay(10000);
}

/**@brief LoRa function for handling HasJoined event.
*/
void lorawan_has_joined_handler(void) {
Serial.println("OTAA Mode, Network Joined!");
}

/**@brief LoRa function for handling OTAA join failed
*/
static void lorawan_join_failed_handler(void) {
Serial.println("OTAA join failed!");
Serial.println("Check your EUI's and Keys's!");
Serial.println("Check if a Gateway is in range!");
}
/**@brief Function for handling LoRaWan received data from Gateway
*
* @param[in] app_data Pointer to rx data
*/
void lorawan_rx_handler(lmh_app_data_t *app_data) {
Serial.printf("LoRa Packet received on port %d, size:%d, rssi:%d, snr:%d, data:%s\n",
app_data->port, app_data->buffsize, app_data->rssi, app_data->snr, app_data->buffer);
}

void lorawan_confirm_class_handler(DeviceClass_t Class) {
Serial.printf("switch to class %c done\n", "ABC"[Class]);
// Informs the server that switch has occurred ASAP
m_lora_app_data.buffsize = 0;
m_lora_app_data.port = gAppPort;
lmh_send(&m_lora_app_data, g_CurrentConfirm);
}

String data = "";

void send_lora_frame(void) {
if (lmh_join_status_get() != LMH_SET) {
//Not joined, try again later
return;
}

Serial.print("result: ");
uint32_t i = 0;
memset(m_lora_app_data.buffer, 0, LORAWAN_APP_DATA_BUFF_SIZE);
m_lora_app_data.port = gAppPort;

// Showing only UV Index (uvi), UV Intensity (uvs) and Lux (lx)
data = " Uvi Data: " + String(uvi) + " Uvs Data: " + String(uvs) + " Lux Data: " + String(lux) + " lx " ;

Serial.println(data);

// Showing only UV Index (uvi), UV Intensity (uvs) and Lux (lx)
uint16_t uvi_data = uvi; // Uvi data from RAK12019
uint32_t uvs_data = uvs; // Uvs data from RAK12019
uint32_t lux_data = lux; // Lux data from RAK12010

m_lora_app_data.buffer[i++] = 0x01; // byte[0]

// Showing only UV Index (uvi), UV Intensity (uvs) and Lux (lx)
// Data from RAK12019 module
m_lora_app_data.buffer[i++] = (uint8_t)((uvi_data & 0x0000FF00) >> 8); // byte[1]
m_lora_app_data.buffer[i++] = (uint8_t)(uvi_data & 0x000000FF); // byte[2]

// Data from RAK12019 module
m_lora_app_data.buffer[i++] = (uint8_t)((uvs_data & 0xFF000000) >> 24); // byte[3]
m_lora_app_data.buffer[i++] = (uint8_t)((uvs_data & 0x00FF0000) >> 16); // byte[4]
m_lora_app_data.buffer[i++] = (uint8_t)((uvs_data & 0x0000FF00) >> 8); // byte[5]
m_lora_app_data.buffer[i++] = (uint8_t)(uvs_data & 0x000000FF); // byte[6]

// Data from RAK12010 module
m_lora_app_data.buffer[i++] = (uint8_t)((lux_data & 0xFF000000) >> 24); // byte[7]
m_lora_app_data.buffer[i++] = (uint8_t)((lux_data & 0x00FF0000) >> 16); // byte[8]
m_lora_app_data.buffer[i++] = (uint8_t)((lux_data & 0x0000FF00) >> 8); // byte[9]
m_lora_app_data.buffer[i++] = (uint8_t)(lux_data & 0x000000FF); // byte[10]

m_lora_app_data.buffsize = i;

lmh_error_status error = lmh_send(&m_lora_app_data, g_CurrentConfirm);
if (error == LMH_SUCCESS) {
count++;
Serial.printf("lmh_send ok count %d\n", count);
} else {
count_fail++;
Serial.printf("lmh_send fail count %d\n", count_fail);
}
}

Before uploading the Arduino Code, there are configurations that you need to set up to ensure that the device can join a LoRaWAN Network server. The steps below will explain the default settings and how to configure them.

  • Set up the LoRaWAN region. The LORAMAC_REGION can be any of your desired region to work with. You can change this to a region that is applicable to you like LORAMAC_REGION_US915, LORAMAC_REGION_AU915, etc. Below is the table of LoRaWAN regions and their respective countries where they are used in:
LoRaWAN RegionUsage
LORAMAC_REGION_AS923-1Australia, Singapore, Solomon Islands, Sri-Lanka, Taiwan
LORAMAC_REGION_AS923-2Vietnam
LORAMAC_REGION_AS923-3Philippines, Albania, Algeria, Denmark, Greenland, Jordan
LORAMAC_REGION_AS923-4Israel
LORAMAC_REGION_AU915Australia, Anguilla, Argentina, and many parts of South America
LORAMAC_REGION_CN470China
LORAMAC_REGION_EU433EU, UK, Brazil, Costa Rica, Cuba, and many parts of Africa
LORAMAC_REGION_IN865India, Cook Islands, Egypt, Hong Kong, Jordan, New Zealand, Niger
LORAMAC_REGION_EU868EU, UK and many parts of Africa
LORAMAC_REGION_KR920Republic of Korea
LORAMAC_REGION_US915USA
LORAMAC_REGION_RU864Russia
LoRaMacRegion_t g_CurrentRegion = LORAMAC_REGION_US915;    /* Region:US915*/
  • Set up the LoRaWAN activation method. In this case, we will be using the OTAA configuration which is also the default from the given code.
bool doOTAA = true;   // OTAA is used by default.
  • Set up the message type if confirmed or not. Confirmed message is the default for this one. You can change to an unconfirmed message by changing the value to LMH_UNCONFIRMED_MSG.
lmh_confirm g_CurrentConfirm = LMH_CONFIRMED_MSG;         /* confirm/unconfirm packet definition*/
  • Set the device to Class A.
DeviceClass_t g_CurrentClass = CLASS_A;         /* class definition*/
  • Setup the EUIs and KEY. The DeviceEUI, AppEUI and AppKey are the credentials of your device registered to TTN that will be used for the OTAA keys in the code. You need to replace the ones in the code with the credentials registered in TTN.

//OTAA keys !!!! KEYS ARE MSB !!!!
uint8_t nodeDeviceEUI[8] = { 0xAC, 0x1F, 0x09, 0xFF, 0xFE, 0x06, 0xD3, 0xE9 };
uint8_t nodeAppEUI[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t nodeAppKey[16] = { 0xE9, 0x0D, 0x28, 0x1D, 0x92, 0x3D, 0xE4, 0x9F, 0xD9, 0xF7, 0xE0, 0x5F, 0x39, 0x10, 0x55, 0x71 };

Figure 1632: OTAA device successfully registered to TTN
  1. Once done with the code, you can now proceed uploading it into your device. You need to select first your RAK4631 board from desktop or laptop. To do this, go to Tools > Board:XXXXX > RAKwireless nRF Boards > WisBlock RAK4631. After you selected your board, you need to select the specific port of your board. To do this, go to Tools > Port > then the specific port of your board.
Figure 1633: Selecting the RAK4631 board
Figure 1634: Selecting the port of RAK4631 board
  1. Once done, you can now upload your code. Simply click the right arrow sign at the upper left portion of your Arduino IDE. Once done, you will see the Device programmed notification at the bottom part of your Arduino IDE.
Figure 1635: Uploading your code into your RAK4631 board
Figure 1636: Arduino code is successfully uploaded into your RAK4631 board
UV Index & UV Intensity Monitoring via TTN
  1. To monitor the data of your UV Index & UV Intensity Monitoring device via TTN, you need to go back to your TTN account where you created your application and registered your device.
Figure 1637: Your UV Index & UV Intensity Monitoring device in TTN
  1. Then go to Payload formatters. Under Formatter type, select Custom Javascript formatter. Then under the Formatter code, you need to replace the default code with the one below. This will decode the data from your device going to TTN. Once done, simply click Save changes.
// LoRaWAN code for RAK12019 + RAK12010 Application

function Decoder(bytes, port)
{

var decoded = {};

if (port === 2)
{
if( bytes[0] == 1) // check if the header byte is 1.
{
uvi_data = (bytes[1] << 8) | (bytes[2]);
uvs_data = (bytes[3] << 24) | (bytes[4] << 16) | (bytes[5] << 8) | (bytes[6]);
lux_data = (bytes[7] << 24) | (bytes[8] << 16) | (bytes[9] << 8) | (bytes[10]);

decoded.uvi = uvi_data;
decoded.uvs = uvs_data;
decoded.lux = lux_data;

return decoded;
}
}
}
Figure 1638: Payload Formatter
  1. Then go back to Live data of your device in TTN and compare it with the live data from the Serial Monitor of your device. You should now seeing identical results between them.
Figure 1639: Live data from your device in TTN
Figure 1640: Live data from your device in its Serial Monitor
UV Index & UV Intensity Monitoring via Qubitro

This section will guide you on how to integrate your application using Qubitro.

  1. Go to Qubitro Portal and create your account.
Figure 1641: Creating qubitro Account
Figure 1642: Creating Qubitro Account
  1. Once done with the account creation, login into your Qubitro Account. Then click New Project, then fill out your desired Name, as well as the Description based on your battery monitoring application, then click Create.
Figure 1643: Creating New Project
Figure 1644: Creating New Project
Figure 1645: Created New Project
  1. Then click into your newly-created project then click New source.
Figure 1646: Adding New source
  1. Among the data sources, choose The Things Stack.
Figure 1647: Choosing The Things Stack
  1. For the integration type, choose Import from network, then copy and paste the PROJECT ID and WEBHOOK SIGNING KEY temporarily to notepad. These credentials will be used on the later part, then click Go to project.
Figure 1648: Copying the credentials
  1. Then head back to your TTN Application where you created your battery monitoring application to add webhook from it. To do this, just click your device then go to Integrations > Webhooks > + Add webhook.
Figure 1649: Going to your TTN application for your UV Monitoring Application
Figure 1650: Adding Webhook
  1. Then choose Qubitro as your webhook template.
Figure 1651: Qubitro
  1. Then fill in the needed details:

    • Webhook ID - For this example, you can use uv-monitoring-application.
    • Project ID - Paste the credential you copied from Step 5.
    • Webhook Signing Key - Paste the credential you copied from Step 5.

    Then click Create Qubitro webhook.

Figure 1652: Creating Qubitro webhook
Figure 1653: Added a Qubitro webhook
  1. After you created your webhook, go back to your Qubitro platform to check the changes made. Refresh Qubitro by clicking the Refresh button on the upper right side of your screen. You will notice that a newly-added device is included in the platform.
Figure 1654: Device successfully included in Qubitro
  1. To add the decoder, simply go to Functions > Create Function. Then under Decoder Function, click Get started. You will be now routed to Function Configuration.
Figure 1655: Creating Function
Figure 1656: Decoder Function
Figure 1657: Function Configuration
  1. Under the Formatter type, choose Custom Javascript formatter. Then under the Formatter code, you need to replace its default entry with the code below:
// LoRaWAN code for RAK12019 + RAK12010 Application

function Decoder(bytes, port)
{

var decoded = {};

if (port === 2)
{
if (bytes[0] == 1) // check if the header byte is 1.
{
uvi_data = (bytes[1] << 8) | (bytes[2]);
uvs_data = (bytes[3] << 24) | (bytes[4] << 16) | (bytes[5] << 8) | (bytes[6]);
lux_data = (bytes[7] << 24) | (bytes[8] << 16) | (bytes[9] << 8) | (bytes[10]);

decoded.uvi = uvi_data;
decoded.uvs = uvs_data;
decoded.lux = lux_data;

return decoded;
}
}
}

Once done, simply click Save and complete.

Figure 1658: Function Configuration
Figure 1659: Created Decoder Function
  1. Then go to your device and click on the Data tab to check for the incoming data coming from your device. You should now be seeing live data from your device. To gather the most recent data, simply click Refresh.
Figure 1660: Data Tab
Figure 1661: Historical Data
Figure 1662: Refreshing data to get newer ones
  1. To add a monitoring dashboard for the data from the UV monitoring device, you need to go to Home which is located at the left top most part of your screen. Then click Dashboards > New dashboard > Create new. Then fill out the Create New Dashboard portion using the details of your device. For the Tags, just simply input Test then click Create. Once done, click on to your newly-created dashboard.
Figure 1663: Creating dashboard for your UV Monitoring device
Figure 1664: Creating dashboard for your UV Monitoring device
Figure 1665: Creating dashboard for your UV Monitoring device
Figure 1666: Creating dashboard for your UV Monitoring device
Figure 1667: Newly-created dashboard for your UV Monitoring device
  1. Click Edit > New widget to add a widget for a specific parameter need to be monitored.
Figure 1668: Adding a widget
Figure 1669: Adding a widget
  1. Once done, you're now at the Widget Configuration. At the WIDGET TYPE, choose Chart. Then provide the name of the parameter under SHOW WIDGET NAME. Once done, proceed to Add point +. Choose your existing project, application and the specific parameter you need to monitor in your dashboard. Once done, just click Save.
Figure 1670: Widget Configuration
Figure 1671: Widget Configuration
Figure 1672: Connect Data Point
Figure 1673: Connect Data Point
Figure 1674: Connect Data Point
Figure 1675: Connect Data Point
Figure 1676: Connect Data Point
Figure 1677: Connect Data Point
Figure 1678: Connect Data Point
  1. Now you have an existing preview of your data from your device. Under CHART TYPE at the left side of your screen, choose Line. Once done, go to STANDARD OPTIONS to choose the appropriate unit for your parameter to be monitored in the dashboard. Once done, click Save widget.
Figure 1679: Widget Configuration
Figure 1680: Widget Configuration
Figure 1681: Widget Configuration
Figure 1682: Widget Configuration
Figure 1683: Saving the changes made in your Widget Configuration
  1. Then click Save changes to include the data of parameter. Then you have now the newly-made widget for your specific parameter.
Figure 1684: Save changes
Figure 1685: Newly-made widget
  1. In able for you to include additional widgets for other parameters, you need to click Edit > New widget then repeat Step 15 to Step 17.
Figure 1686: Adding widgets into your dashboard
Figure 1687: Adding widgets into your dashboard
  1. Then there you have it a real-time monitoring dashboard for your UV monitoring device.
Figure 1688: Monitoring dashboard for your UV monitoring device

Back

Light Color Recognizer LoRaWAN Application

The Light Color Recognizer LoRaWAN Application is used to evaluate the color of the light being monitored using an RGB sensor. It uses RAK12021 which is an RGB sensor which is based on TCS37725FN from AMS. It also uses RAK14001 RGB LED module which will visualize the color being monitored by RAK12021 module.

Figure 1689: Light Color Recognizer device
Light Color Recognizer - TTN Registration Section and Device Registration
  1. If you already have an existing TTN account, you may proceed to the next steps. If you haven't created any TTN account, please refer to this link to create one.

  2. Once done with the TTN account creation, you may now proceed with the device registration. Please refer to this guide for your reference. After creating the application and adding the device in TTN, you can proceed on the LoRaWAN Code uploading steps.

LoRaWAN Code for Light Color Recognizer
  1. If you already have Arduino IDE installed to your laptop or PC and added RAK4631 board into it, you may proceed to the next step. If you haven't yet installed the Arduino IDE, please refer to this link for the steps.

  2. After the installation, you can now proceed programming your Light Color Recognizer device. Just copy the code below for Light Color Recognizer LoRaWAN Application and paste it into the Arduino IDE.

NOTE

The example code uses SX126x-Arduino library which needs to be added to successfully compile the LoRaWAN code.

Click to view the example
#include <Arduino.h>
#include <LoRaWan-RAK4630.h> //http://librarymanager/All#SX126x
#include <Wire.h>
#include <NCP5623.h> //http://librarymanager/All#NCP5623 By:RAKWireless
#include "TCS3772.h" // Click here to get the library: http://librarymanager/All#TCS37725
// It use WB_IO2 to power up and is conflicting with INT1, so better use in SlotA/SlotC/SlotD.

NCP5623 rgb;
TCS3772 tcs3772;
TCS3772_DataScaled tcs3772_data = {0};

uint16_t scale_factor;

uint16_t redColor;
uint16_t greenColor;
uint16_t blueColor;

#ifdef RAK4630
#define BOARD "RAK4631 "
#define RAK4631_BOARD true
#else
#define RAK4631_BOARD false
#endif

bool doOTAA = true; // OTAA is used by default.
#define SCHED_MAX_EVENT_DATA_SIZE APP_TIMER_SCHED_EVENT_DATA_SIZE /**< Maximum size of scheduler events. */
#define SCHED_QUEUE_SIZE 60 /**< Maximum number of events in the scheduler queue. */
#define LORAWAN_DATERATE DR_0 /*LoRaMac datarates definition, from DR_0 to DR_5*/
#define LORAWAN_TX_POWER TX_POWER_5 /*LoRaMac tx power definition, from TX_POWER_0 to TX_POWER_15*/
#define JOINREQ_NBTRIALS 3 /**< Number of trials for the join request. */
DeviceClass_t g_CurrentClass = CLASS_A; /* class definition*/
LoRaMacRegion_t g_CurrentRegion = LORAMAC_REGION_US915; /* Region:EU868*/
lmh_confirm g_CurrentConfirm = LMH_CONFIRMED_MSG; /* confirm/unconfirm packet definition*/
uint8_t gAppPort = LORAWAN_APP_PORT; /* data port*/

/**@brief Structure containing LoRaWan parameters, needed for lmh_init()
*/
static lmh_param_t g_lora_param_init = { LORAWAN_ADR_ON, LORAWAN_DATERATE, LORAWAN_PUBLIC_NETWORK, JOINREQ_NBTRIALS, LORAWAN_TX_POWER, LORAWAN_DUTYCYCLE_OFF };

// Forward declaration
static void lorawan_has_joined_handler(void);
static void lorawan_join_failed_handler(void);
static void lorawan_rx_handler(lmh_app_data_t *app_data);
static void lorawan_confirm_class_handler(DeviceClass_t Class);
static void send_lora_frame(void);

/**@brief Structure containing LoRaWan callback functions, needed for lmh_init()
*/
static lmh_callback_t g_lora_callbacks = { BoardGetBatteryLevel, BoardGetUniqueId, BoardGetRandomSeed,
lorawan_rx_handler, lorawan_has_joined_handler, lorawan_confirm_class_handler, lorawan_join_failed_handler };
//OTAA keys !!!! KEYS ARE MSB !!!!
uint8_t nodeDeviceEUI[8] = { 0xAC, 0x1F, 0x09, 0xFF, 0xFE, 0x05, 0x3E, 0x3E };
uint8_t nodeAppEUI[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t nodeAppKey[16] = { 0x96, 0xBD, 0xC5, 0x98, 0x17, 0x69, 0x8D, 0xFA, 0x1F, 0x64, 0xFE, 0x1C, 0xF9, 0x26, 0x7F, 0x8D };

// ABP keys
uint32_t nodeDevAddr = 0x260116F8;
uint8_t nodeNwsKey[16] = { 0x7E, 0xAC, 0xE2, 0x55, 0xB8, 0xA5, 0xE2, 0x69, 0x91, 0x51, 0x96, 0x06, 0x47, 0x56, 0x9D, 0x23 };
uint8_t nodeAppsKey[16] = { 0xFB, 0xAC, 0xB6, 0x47, 0xF3, 0x58, 0x45, 0xC7, 0x50, 0x7D, 0xBF, 0x16, 0x8B, 0xA8, 0xC1, 0x7C };

// Private definition
#define LORAWAN_APP_DATA_BUFF_SIZE 64 /**< buffer size of the data to be transmitted. */
static uint8_t m_lora_app_data_buffer[LORAWAN_APP_DATA_BUFF_SIZE]; //< Lora user application data buffer.
static lmh_app_data_t m_lora_app_data = { m_lora_app_data_buffer, 0, 0, 0, 0 }; //< Lora user application data structure.

static uint32_t count = 0;
static uint32_t count_fail = 0;

void setup() {

// Initialize Serial for debug output
pinMode(LED_BLUE, OUTPUT);
digitalWrite(LED_BLUE, HIGH);

// Sensor Power Switch
pinMode(WB_IO2, OUTPUT);
digitalWrite(WB_IO2, HIGH);

// enable RAK14001
pinMode(WB_IO6, OUTPUT);
digitalWrite(WB_IO6, HIGH);

// Initialize LoRa chip.
lora_rak4630_init();

// Initialize Serial for debug output
time_t timeout = millis();
Serial.begin(115200);
while (!Serial) {
if ((millis() - timeout) < 5000) {
delay(100);
} else {
break;
}
}
Serial.println("=====================================");
Serial.println("Welcome to RAK4630 LoRaWan!!!");
if (doOTAA) {
Serial.println("Type: OTAA");
} else {
Serial.println("Type: ABP");
}

switch (g_CurrentRegion) {
case LORAMAC_REGION_AS923:
Serial.println("Region: AS923");
break;
case LORAMAC_REGION_AU915:
Serial.println("Region: AU915");
break;
case LORAMAC_REGION_CN470:
Serial.println("Region: CN470");
break;
case LORAMAC_REGION_EU433:
Serial.println("Region: EU433");
break;
case LORAMAC_REGION_IN865:
Serial.println("Region: IN865");
break;
case LORAMAC_REGION_EU868:
Serial.println("Region: EU868");
break;
case LORAMAC_REGION_KR920:
Serial.println("Region: KR920");
break;
case LORAMAC_REGION_US915:
Serial.println("Region: US915");
break;
}
Serial.println("=====================================");

// Setup the EUIs and Keys
if (doOTAA) {
lmh_setDevEui(nodeDeviceEUI);
lmh_setAppEui(nodeAppEUI);
lmh_setAppKey(nodeAppKey);
} else {
lmh_setNwkSKey(nodeNwsKey);
lmh_setAppSKey(nodeAppsKey);
lmh_setDevAddr(nodeDevAddr);
}

uint32_t err_code;
// Initialize LoRaWan
err_code = lmh_init(&g_lora_callbacks, g_lora_param_init, doOTAA, g_CurrentClass, g_CurrentRegion);
if (err_code != 0) {
Serial.printf("lmh_init failed - %d\n", err_code);
return;
}

// Start Join procedure
lmh_join();

Serial.println("================================");
Serial.println("RAK12021 + RAK14001 LoRaWAN Code");
Serial.println("================================");

// If using Native I2C
Wire.begin();
Wire.setClock(100000);

// Serial.println("RAK14001 + RAK12021");

if (!rgb.begin())
{
Serial.println("RAK14001 not found on the I2C line");
while (1);
}
else
{
Serial.println("RAK14001 Found. Begining execution");
}

// set the current output level max, the range is 1 to 31
rgb.setCurrent(25);

if(tcs3772.begin() == true)
{
Serial.println("Found sensor.");
}
else
{
Serial.println("TCS37725 not found ... check your connections.");
while(1)
{
delay(10);
}
}
delay(1000);
}

void loop() {
// Put your application tasks here, like reading of sensors,
// Controlling actuators and/or other functions.

tcs3772_data = tcs3772.getMeasurement();

scale_factor = tcs3772.autoGain(tcs3772_data.clear);

redColor = tcs3772_data.red;
greenColor = tcs3772_data.green;
blueColor = tcs3772_data.blue;

rgb.setColor(0,0,0); // Initially OFF

Serial.println("Sending frame now...");
send_lora_frame();

Serial.print(" R: ");
Serial.println(redColor);
Serial.print(" G: ");
Serial.println(greenColor);
Serial.print(" B: ");
Serial.println(blueColor);

// The values of redColor, greenColor and blueColor can be varied during the sensing calibration of RAK12021 module

if (((redColor >= 9000) && (redColor <= 65535)) && ((greenColor >= 10000) && (greenColor <= 65535)) && ((blueColor >= 12000) && (blueColor <= 65535)))
{
rgb.setColor(255,255,255); // WHITE
}

else if (((redColor >= 4000) && (redColor <= 18000)) && ((greenColor >= 1300) && (greenColor <= 5000)) && ((blueColor >= 950) && (blueColor <= 2000)))
{
rgb.setColor(255,255,0); // YELLOW
}

else if (((redColor >= 600) && (redColor <= 2000)) && ((greenColor >= 1700) && (greenColor <= 10000)) && ((blueColor >= 6800) && (blueColor <= 27000)))
{
rgb.setColor(0,0,255); // BLUE
}

else if (((redColor >= 1400) && (redColor <= 4000)) && ((greenColor >= 1300) && (greenColor <= 10000)) && ((blueColor >= 950) && (blueColor <= 2700)))
{
rgb.setColor(0,255,0); // GREEN
}

else if (((redColor >= 3000) && (redColor <= 20000)) && ((greenColor >= 700) && (greenColor <= 2400)) && ((blueColor >= 950) && (blueColor <= 3000)))
{
rgb.setColor(255,0,0); // RED
}

else if ((redColor < 1500) && (greenColor < 1400) && (blueColor < 900))
{
rgb.setColor(0,0,0); // OFF
}

delay(5000);
}

/**@brief LoRa function for handling HasJoined event.
*/
void lorawan_has_joined_handler(void) {
Serial.println("OTAA Mode, Network Joined!");
}

/**@brief LoRa function for handling OTAA join failed
*/
static void lorawan_join_failed_handler(void) {
Serial.println("OTAA join failed!");
Serial.println("Check your EUI's and Keys's!");
Serial.println("Check if a Gateway is in range!");
}
/**@brief Function for handling LoRaWan received data from Gateway
*
* @param[in] app_data Pointer to rx data
*/
void lorawan_rx_handler(lmh_app_data_t *app_data) {
Serial.printf("LoRa Packet received on port %d, size:%d, rssi:%d, snr:%d, data:%s\n",
app_data->port, app_data->buffsize, app_data->rssi, app_data->snr, app_data->buffer);
}

void lorawan_confirm_class_handler(DeviceClass_t Class) {
Serial.printf("switch to class %c done\n", "ABC"[Class]);
// Informs the server that switch has occurred ASAP
m_lora_app_data.buffsize = 0;
m_lora_app_data.port = gAppPort;
lmh_send(&m_lora_app_data, g_CurrentConfirm);
}

String data = "";

void send_lora_frame(void) {
if (lmh_join_status_get() != LMH_SET) {
//Not joined, try again later
return;
}

Serial.print("result: ");
uint32_t i = 0;
memset(m_lora_app_data.buffer, 0, LORAWAN_APP_DATA_BUFF_SIZE);
m_lora_app_data.port = gAppPort;

// Showing the values of red, green and blue colors from RAK12021
data = " Red: " + String(redColor) + " Green: " + String(greenColor) + " Blue: " + String(blueColor);

Serial.println(data);

uint16_t red_data = redColor;
uint16_t green_data = greenColor;
uint16_t blue_data = blueColor;

m_lora_app_data.buffer[i++] = 0x01; // byte[0]

// Red data
m_lora_app_data.buffer[i++] = (uint8_t)((red_data & 0x0000FF00) >> 8); // byte[1]
m_lora_app_data.buffer[i++] = (uint8_t)(red_data & 0x000000FF); // byte[2]

// Green data
m_lora_app_data.buffer[i++] = (uint8_t)((green_data & 0x0000FF00) >> 8); // byte[3]
m_lora_app_data.buffer[i++] = (uint8_t)(green_data & 0x000000FF); // byte[4]

// Blue data
m_lora_app_data.buffer[i++] = (uint8_t)((blue_data & 0x0000FF00) >> 8); // byte[5]
m_lora_app_data.buffer[i++] = (uint8_t)(blue_data & 0x000000FF); // byte[6]

m_lora_app_data.buffsize = i;

lmh_error_status error = lmh_send(&m_lora_app_data, g_CurrentConfirm);
if (error == LMH_SUCCESS) {
count++;
Serial.printf("lmh_send ok count %d\n", count);
} else {
count_fail++;
Serial.printf("lmh_send fail count %d\n", count_fail);
}
}

Before uploading the Arduino Code, there are configurations that you need to set up to ensure that the device can join a LoRaWAN Network server. The steps below will explain the default settings and how to configure them.

  • Set up the LoRaWAN region. The LORAMAC_REGION can be any of your desired region to work with. You can change this to a region that is applicable to you like LORAMAC_REGION_US915, LORAMAC_REGION_AU915, etc. Below is the table of LoRaWAN regions and their respective countries where they are used in:
LoRaWAN RegionUsage
LORAMAC_REGION_AS923-1Australia, Singapore, Solomon Islands, Sri-Lanka, Taiwan
LORAMAC_REGION_AS923-2Vietnam
LORAMAC_REGION_AS923-3Philippines, Albania, Algeria, Denmark, Greenland, Jordan
LORAMAC_REGION_AS923-4Israel
LORAMAC_REGION_AU915Australia, Anguilla, Argentina, and many parts of South America
LORAMAC_REGION_CN470China
LORAMAC_REGION_EU433EU, UK, Brazil, Costa Rica, Cuba, and many parts of Africa
LORAMAC_REGION_IN865India, Cook Islands, Egypt, Hong Kong, Jordan, New Zealand, Niger
LORAMAC_REGION_EU868EU, UK and many parts of Africa
LORAMAC_REGION_KR920Republic of Korea
LORAMAC_REGION_US915USA
LORAMAC_REGION_RU864Russia
LoRaMacRegion_t g_CurrentRegion = LORAMAC_REGION_US915;    /* Region:US915*/
  • Set up the LoRaWAN activation method. In this case, we will be using the OTAA configuration which is also the default from the given code.
bool doOTAA = true;   // OTAA is used by default.
  • Set up the message type if confirmed or not. Confirmed message is the default for this one. You can change to an unconfirmed message by changing the value to LMH_UNCONFIRMED_MSG.
lmh_confirm g_CurrentConfirm = LMH_CONFIRMED_MSG;         /* confirm/unconfirm packet definition*/
  • Set the device to Class A.
DeviceClass_t g_CurrentClass = CLASS_A;         /* class definition*/
  • Setup the EUIs and KEY. The DeviceEUI, AppEUI and AppKey are the credentials of your device registered to TTN that will be used for the OTAA keys in the code. You need to replace the ones in the code with the credentials registered in TTN.
//OTAA keys !!!! KEYS ARE MSB !!!!
uint8_t nodeDeviceEUI[8] = { 0xAC, 0x1F, 0x09, 0xFF, 0xFE, 0x05, 0x3E, 0x3E };
uint8_t nodeAppEUI[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t nodeAppKey[16] = { 0x96, 0xBD, 0xC5, 0x98, 0x17, 0x69, 0x8D, 0xFA, 0x1F, 0x64, 0xFE, 0x1C, 0xF9, 0x26, 0x7F, 0x8D };
Figure 1690: OTAA device successfully registered to TTN
  1. Once done with the code, you can now proceed uploading it into your device. You need to select first your RAK4631 board from desktop or laptop. To do this, go to Tools > Board:XXXXX > RAKwireless nRF Boards > WisBlock RAK4631. After you selected your board, you need to select the specific port of your board. To do this, go to Tools > Port > then the specific port of your board.
Figure 1691: Selecting the RAK4631 board
Figure 1692: Selecting the port of RAK4631 board
  1. Once done, you can now upload your code. Simply click the right arrow sign at the upper left portion of your Arduino IDE. Once done, you will see the Device programmed notification at the bottom part of your Arduino IDE.
Figure 1693: Uploading your code into your RAK4631 board
Figure 1694: Arduino code is successfully uploaded into your RAK4631 board
Light Color Recognizer via TTN
  1. To monitor the data of your Light Color Recognizer device via TTN, you need to go back to your TTN account where you created your application and registered your device.
Figure 1695: Your Light Color Recognizer device in TTN
  1. Then go to Payload formatters. Under Formatter type, select Custom Javascript formatter. Then under the Formatter code, you need to replace the default code with the one below. This will decode the data from your device going to TTN. Once done, simply click Save changes.
// LoRaWAN code for RAK12021 + RAK14001 Application

function Decoder(bytes, port)
{

var decoded = {};

if (port === 2)
{
if( bytes[0] == 1) // check if the header byte is 1.
{
red_data = (bytes[1] << 8) | (bytes[2]);
green_data = (bytes[3] << 8) | (bytes[4]);
blue_data = (bytes[5] << 8) | (bytes[6]);

decoded.red = red_data;
decoded.green = green_data;
decoded.blue = blue_data;

return decoded;
}
}
}
Figure 1696: Payload Formatter
  1. Then go back to Live data of your device in TTN and compare it with the live data from the Serial Monitor of your device. You should now seeing identical results between them.
Figure 1697: Live data from your device in TTN
Figure 1698: Live data from your device in its Serial Monitor
Light Color Recognizer via Qubitro

This section will guide you on how to integrate your application using Qubitro.

  1. Go to Qubitro Portal and create your account.
Figure 1699: Creating Qubitro Account
Figure 1700: Creating Qubitro Account
  1. Once done with the account creation, login into your Qubitro Account. Then click New Project, then fill out your desired Name, as well as the Description based on your light color recognizer application, then click Create.
Figure 1701: Creating New Project
Figure 1702: Creating New Project
Figure 1703: Created New Project
  1. Then click into your newly-created project then click New source.
Figure 1704: Adding New source
  1. Among the data sources, choose The Things Stack.
Figure 1705: Choosing The Things Stack
  1. For the integration type, choose Import from network, then copy and paste the PROJECT ID and WEBHOOK SIGNING KEY temporarily to notepad. These credentials will be used on the later part, then click Go to project.
Figure 1706: Copying the credentials
  1. Then head back to your TTN Application where you created your light color recognizer application to add webhook from it. To do this, just click your device then go to Integrations > Webhooks > + Add webhook.
Figure 1707: Going to your TTN application for your Light Color Recognizer Application
Figure 1708: Adding Webhook
  1. Then choose Qubitro as your webhook template.
Figure 1709: Qubitro
  1. Then fill in the needed details:

    • Webhook ID - For this example, you can use light-color-recognizer-application.
    • Project ID - Paste the credential you copied from Step 5.
    • Webhook Signing Key - Paste the credential you copied from Step 5.

    Then click Create Qubitro webhook.

Figure 1710: Creating Qubitro webhook
Figure 1711: Added a Qubitro webhook
  1. After you created your webhook, go back to your Qubitro platform to check the changes made. Refresh Qubitro by clicking the Refresh button on the upper right side of your screen. You will notice that a newly-added device is included in the platform.
Figure 1712: Device successfully included in Qubitro
  1. To add the decoder, simply go to Functions > Create Function. Then under Decoder Function, click Get started. You will be now routed to Function Configuration.
Figure 1713: Creating Function
Figure 1714: Decoder Function
Figure 1715: Function Configuration
  1. Under the Formatter type, choose Custom Javascript formatter. Then under the Formatter code, you need to replace its default entry with the code below:
// LoRaWAN code for RAK12021 + RAK14001 Application

function Decoder(bytes, port)
{

var decoded = {};

if (port === 2)
{
if( bytes[0] == 1) // check if the header byte is 1.
{
red_data = (bytes[1] << 8) | (bytes[2]);
green_data = (bytes[3] << 8) | (bytes[4]);
blue_data = (bytes[5] << 8) | (bytes[6]);

decoded.red = red_data;
decoded.green = green_data;
decoded.blue = blue_data;

return decoded;
}
}
}

Once done, simply click Save and complete.

Figure 1716: Function Configuration
Figure 1717: Created Decoder Function
  1. Then go to your device and click on the Data tab to check for the incoming data coming from your device. You should now be seeing live data from your device. To gather the most recent data, simply click Refresh.
Figure 1718: Data Tab
Figure 1719: Historical Data
Figure 1720: Refreshing data to get newer ones
  1. To add a monitoring dashboard for the data from the light color recognizer device, you need to go to Home which is located at the left top most part of your screen. Then click Dashboards > New dashboard > Create new. Then fill out the Create New Dashboard portion using the details of your device. For the Tags, just simply input Test then click Create. Once done, click on to your newly-created dashboard.
Figure 1721: Creating dashboard for your Light Color Recognizer device
Figure 1722: Creating dashboard for your Light Color Recognizer device
Figure 1723: Creating dashboard for your Light Color Recognizer device
Figure 1724: Creating dashboard for your Light Color Recognizer device
Figure 1725: Newly-created dashboard for your Light Color Recognizer device
  1. Click Edit > New widget to add a widget for a specific parameter need to be monitored.
Figure 1726: Adding a widget
Figure 1727: Adding a widget
  1. Once done, you're now at the Widget Configuration. At the WIDGET TYPE, choose Chart. Then provide the name of the parameter under SHOW WIDGET NAME. Once done, proceed to Add point +. Choose your existing project, application and the specific parameter you need to monitor in your dashboard. Once done, just click Save.
Figure 1728: Widget Configuration
Figure 1729: Widget Configuration
Figure 1730: Connect Data Point
Figure 1731: Connect Data Point
Figure 1732: Connect Data Point
Figure 1733: Connect Data Point
Figure 1734: Connect Data Point
Figure 1735: Connect Data Point
Figure 1736: Connect Data Point
  1. Now you have an existing preview of your data from your device. Under CHART TYPE at the left side of your screen, choose Bar. Also, you can modify the color of your bar graph at the STYLE section. Once done, go to STANDARD OPTIONS to choose the appropriate unit for your parameter to be monitored in the dashboard. Once done, click Save widget.
Figure 1737: Widget Configuration
Figure 1738: Widget Configuration
Figure 1739: Widget Configuration
Figure 1740: Widget Configuration
Figure 1741: Widget Configuration
Figure 1742: Saving the changes made in your Widget Configuration
  1. Then click Save changes to include the data of parameter. Then you have now the newly-made widget for your specific parameter.
Figure 1743: Save changes
Figure 1744: Newly-made widget
  1. In able for you to include additional widgets for other parameters, you need to click Edit > New widget then repeat Step 15 to Step 17.
Figure 1745: Adding widgets into your dashboard
Figure 1746: Adding widgets into your dashboard
  1. Then there you have it a real-time monitoring dashboard for your light color recognizer device.
Figure 1747: Monitoring dashboard for your light color recognizer device

Back

Miscellaneous

TTN Account Creation

  1. The first step is to go to The Things Network and sign up an account shown in the figure below. Then select a cluster as shown in the figure below.
Figure 1748: Signing up an account in TTN
Figure 1749: Signing up an account in TTN
Figure 1750: Selecting Cluster in TTN
Figure 1751: Signing up through the Things ID
Figure 1752: Creation of an account through the Things ID
Figure 1753: Creation of an account through the Things ID

You can use the same login credentials on the TTN V2 if you have one. If you have no account yet, you need to create one.

  1. Now that you are logged in to the platform, the next step is to create an application. Click Create an application.
Figure 1754: The Things Stack Platform
Figure 1755: Creating TTN Application of your LoRaWAN devices
  1. To have an application registered, input first the specific details and necessary information about your application then click Create application.
Figure 1756: Details of the TTN application
NOTE

The details and information are dependent to what device you are using (e.g. RAK12019, etc.).

  1. If you have no error on the previous step, you should now be on the application console page.
NOTE

Above procedures are applicable to all applications you will be using.

  • For UV Index & UV Intensity Monitoring, go to this link once done with the TTN account creation.
  • For Light Color Recognizer, go to this link once done with the TTN account creation.

Device Registration

  1. Go to your application console to register a device. To start adding an OTAA end-device, click + Register end device, as shown below.
Figure 1757: Register End Device
  1. To register the board, click the Enter end device specifics manually.
Figure 1758: Enter end device specifics manually
  1. Next step is to set up Frequency plan, compatible LoRaWAN version, and Regional Parameters version supported. Then provide the JoinEUI credentials by entering zeroes into it.
Figure 1759: Setting up your device
Figure 1760: Setting up your device
  1. Then click Show advanced activation, LoRaWAN class and cluster settings. Configure the activation mode by selecting Over the air activation (OTAA) and Additional LoRaWAN class capabilities to class A only. Then click Confirm.
Figure 1761: Setting up your device
Figure 1762: Setting up your device
  1. Once done, provide the DevEUI credentials of your device into the DevEUI portion. This will automatically generate the specific End device ID of your board. Then click Generate under AppKey under Provisioning information section. Once done, you need to change the End device ID since it is automatically prefilled using the DevEUI of your device. Then click Register end device.
NOTE
  • The AppEUI, DevEUI, and AppKey are hidden in this section as these are unique from a specific device. The DevEUI credential is unique to every RAK4631 device. Also, you should generate your own AppEUI and AppKey credentials for your specific device and application.

  • The AppEUI is the same as JoinEUI.

  • The details under End device ID are dependent to what device you are using (e.g. uv-intensity-meter, etc.).

Figure 1763: Setting up your device
Figure 1764: Setting up your device
Figure 1765: Changing the End device ID
Figure 1766: Register End Device
  1. You should now be able to see the device on the TTN console after you fully register your device, as shown below.
NOTE
  • The AppEUI, DevEUI, and AppKey are the parameters that you will need to activate your LoRaWAN end-device via OTAA. The AppKey is hidden by default for security reasons, but you can easily show it by clicking the show button. You can also copy the parameters quickly using the copy button.

  • These parameters are always accessible on the device console page, as highlighted in the figure below.

Figure 1767: OTAA device successfully registered to TTN
NOTE

Above procedures are applicable to all applications you will be using.

  • For UV Index & UV Intensity Monitoring, go to this link once done with the device registration.
  • For Light Color Recognizer, go to this link once done with the device registration.

Arduino IDE Installation + RAK4631

  1. Download the Arduino IDE and install it on your PC or laptop. You must choose the appropriate Arduino IDE depending on your operating system.
Figure 1768: Download Options for the Arduino IDE
  1. Open the Arduino IDE then install the RAKwireless Arduino BSP for WisBlock by using the package_rakwireless_index.json board installation package. The WisBlock Core should now be available on the Arduino IDE. Click on File > Preference. In the Preference window, look for Additional Boards Manager URLs then click the icon on the right side. Paste the link into it then click OK > OK.
Figure 1769: Preference Set-Up
Figure 1770: Preference Window
Figure 1771: RAKwireless Arduino BSP
Figure 1772: Completing the setup of the RAKwireless BSP support for the Arduino Board Manager
  1. Then to your Arduino IDE, go to Tools > Board:XXXXX > Boards Manager. Then look for RAKwireless Boards by RAKwireless since we will be working on with the RAK4631 WisBlock Core. Choose the latest version then install it. Once done, close the Board Manager.
Figure 1773: Opening the Boards Manager
Figure 1774: Installing the RAKwireless nRF Boards
Figure 1775: Successfully installed the RAKwireless nRF Boards
NOTE

Above procedures are applicable to all applications you will be using.

  • For UV Index & UV Intensity Monitoring, go to this link once done with the device registration.
  • For Light Color Recognizer, go to this link once done with the device registration.