In this example we will develop an IoT project consisting on a meteorological station connected to an Oficloud gateway by means of LoRa technology.
We will take advantage of the Node JS open source repository of IoT libraries for this purpose.
NOTE: This example is exclusively for educational purposes. The goal is not to produce a functional meteorological station but to learn how to integrate Oficloud with any similar meteorological station and how to integrate LoRa technology on any Oficloud project.
The materials needed are:
You also need to choose between one of these options:
STEPS
/**
* Oficloud Meteo Station example with LoRa transceiver
* @author Oficloud SL (www.oficloud.com)
* @date 1 February 2019
*/
/*
All the resources for this project:
http://randomnerdtutorials.com/
*/
#include "DHT.h"
// Uncomment whatever type you're using!
//#define DHTTYPE DHT11 // DHT 11
#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
//#define DHTTYPE DHT21 // DHT 21 (AM2301)
// Connect pin 1 (on the left) of the sensor to +5V
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
// to 3.3V instead of 5V!
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor
const int DHTPin = 5; // what digital pin we're connected to
DHT dht(DHTPin, DHTTYPE);
int rainPin = A1;
int greenLED = 6;
int redLED = 7;
// you can adjust the threshold value
int thresholdValue = 500;
bool g_is_wet=false;
/**
* E32-TTL-100 Transceiver Interface
*
* @author Bob Chen (bob-0505@gotmail.com)
* @date 1 November 2017
* https://github.com/Bob0505/E32-TTL-100
*/
#include <SoftwareSerial.h<
#include <JeeLib.h<
#include "E32-TTL-100.h"
#undef Device_A
ISR(WDT_vect) { Sleepy::watchdogEvent(); } // Setup the watchdog
/*
need series a 4.7k Ohm resistor between .
UNO/NANO(5V mode) E32-TTL-100
*--------* *------*
| D7 | <------------------< | M0 |
| D8 | <------------------< | M1 |
| A0 | <------------------< | AUX |
| D10(Rx)| <---< 4.7k Ohm <---< | Tx |
| D11(Tx)| <---< 4.7k Ohm <---< | Rx |
*--------* *------*
*/
#define M0_PIN 7
#define M1_PIN 8
#define AUX_PIN A0
#define SOFT_RX 10
#define SOFT_TX 11
#define LED_BUTTON 2
SoftwareSerial softSerial(SOFT_RX, SOFT_TX); // RX, TX
//=== AUX ===========================================+
bool AUX_HL;
bool ReadAUX()
{
int val = analogRead(AUX_PIN);
if(val<50)
{
AUX_HL = LOW;
}else {
AUX_HL = HIGH;
}
return AUX_HL;
}
//return default status
RET_STATUS WaitAUX_H()
{
RET_STATUS STATUS = RET_SUCCESS;
uint8_t cnt = 0;
uint8_t data_buf[100], data_len;
while((ReadAUX()==LOW) && (cnt++<TIME_OUT_CNT))
{
Serial.print(".");
delay(100);
}
if(cnt==0)
{
}
else if(cnt<=TIME_OUT_CNT)
{
STATUS = RET_TIMEOUT;
Serial.println(" TimeOut");
}
else
{
Serial.println("");
}
return STATUS;
}
//=== AUX ===========================================-
//=== Mode Select ===================================+
bool chkModeSame(MODE_TYPE mode)
{
static MODE_TYPE pre_mode = MODE_INIT;
if(pre_mode == mode)
{
//Serial.print("SwitchMode: (no need to switch) "); Serial.println(mode, HEX);
return true;
}
else
{
Serial.print("SwitchMode: from "); Serial.print(pre_mode, HEX); Serial.print(" to "); Serial.println(mode, HEX);
pre_mode = mode;
return false;
}
}
void SwitchMode(MODE_TYPE mode)
{
if(!chkModeSame(mode))
{
WaitAUX_H();
switch (mode)
{
case MODE_0_NORMAL:
// Mode 0 | normal operation
digitalWrite(M0_PIN, LOW);
digitalWrite(M1_PIN, LOW);
break;
case MODE_1_WAKE_UP:
digitalWrite(M0_PIN, HIGH);
digitalWrite(M1_PIN, LOW);
break;
case MODE_2_POWER_SAVIN:
digitalWrite(M0_PIN, LOW);
digitalWrite(M1_PIN, HIGH);
break;
case MODE_3_SLEEP:
// Mode 3 | Setting operation
digitalWrite(M0_PIN, HIGH);
digitalWrite(M1_PIN, HIGH);
break;
default:
return ;
}
WaitAUX_H();
delay(10);
}
}
//=== Mode Select ===================================-
//=== Basic cmd =====================================+
void cleanUARTBuf()
{
bool IsNull = true;
while (softSerial.available())
{
IsNull = false;
softSerial.read();
}
}
void triple_cmd(SLEEP_MODE_CMD_TYPE Tcmd)
{
uint8_t CMD[3] = {Tcmd, Tcmd, Tcmd};
softSerial.write(CMD, 3);
delay(50); //need ti check
}
RET_STATUS Module_info(uint8_t* pReadbuf, uint8_t buf_len)
{
RET_STATUS STATUS = RET_SUCCESS;
uint8_t Readcnt, idx;
Readcnt = softSerial.available();
//Serial.print("softSerial.available(): "); Serial.print(Readcnt); Serial.println(" bytes.");
if (Readcnt == buf_len)
{
for(idx=0;idx<buf_len;idx++)
{
*(pReadbuf+idx) = softSerial.read();
Serial.print(" 0x");
Serial.print(0xFF & *(pReadbuf+idx), HEX); // print as an ASCII-encoded hexadecimal
} Serial.println("");
}
else
{
STATUS = RET_DATA_SIZE_NOT_MATCH;
Serial.print(" RET_DATA_SIZE_NOT_MATCH - Readcnt: "); Serial.println(Readcnt);
cleanUARTBuf();
}
return STATUS;
}
//=== Basic cmd =====================================-
//=== Sleep mode cmd ================================+
RET_STATUS Write_CFG_PDS(struct CFGstruct* pCFG)
{
softSerial.write((uint8_t *)pCFG, 6);
WaitAUX_H();
delay(1200); //need ti check
return RET_SUCCESS;
}
RET_STATUS Read_CFG(struct CFGstruct* pCFG)
{
RET_STATUS STATUS = RET_SUCCESS;
//1. read UART buffer.
cleanUARTBuf();
//2. send CMD
triple_cmd(R_CFG);
//3. Receive configure
STATUS = Module_info((uint8_t *)pCFG, sizeof(CFGstruct));
if(STATUS == RET_SUCCESS)
{
Serial.print(" HEAD: "); Serial.println(pCFG-<HEAD, HEX);
Serial.print(" ADDH: "); Serial.println(pCFG-<ADDH, HEX);
Serial.print(" ADDL: "); Serial.println(pCFG-<ADDL, HEX);
Serial.print(" CHAN: "); Serial.println(pCFG-<CHAN, HEX);
}
return STATUS;
}
RET_STATUS Read_module_version(struct MVerstruct* MVer)
{
RET_STATUS STATUS = RET_SUCCESS;
//1. read UART buffer.
cleanUARTBuf();
//2. send CMD
triple_cmd(R_MODULE_VERSION);
//3. Receive configure
STATUS = Module_info((uint8_t *)MVer, sizeof(MVerstruct));
if(STATUS == RET_SUCCESS)
{
Serial.print(" HEAD: 0x"); Serial.println(MVer-<HEAD, HEX);
Serial.print(" Model: 0x"); Serial.println(MVer-<Model, HEX);
Serial.print(" Version: 0x"); Serial.println(MVer-<Version, HEX);
Serial.print(" features: 0x"); Serial.println(MVer-<features, HEX);
}
return RET_SUCCESS;
}
void Reset_module()
{
triple_cmd(W_RESET_MODULE);
WaitAUX_H();
delay(1000);
}
RET_STATUS SleepModeCmd(uint8_t CMD, void* pBuff)
{
RET_STATUS STATUS = RET_SUCCESS;
Serial.print("SleepModeCmd: 0x"); Serial.println(CMD, HEX);
WaitAUX_H();
SwitchMode(MODE_3_SLEEP);
switch (CMD)
{
case W_CFG_PWR_DWN_SAVE:
STATUS = Write_CFG_PDS((struct CFGstruct* )pBuff);
break;
case R_CFG:
STATUS = Read_CFG((struct CFGstruct* )pBuff);
break;
case W_CFG_PWR_DWN_LOSE:
break;
case R_MODULE_VERSION:
Read_module_version((struct MVerstruct* )pBuff);
break;
case W_RESET_MODULE:
Reset_module();
break;
default:
return RET_INVALID_PARAM;
}
WaitAUX_H();
return STATUS;
}
//=== Sleep mode cmd ================================-
RET_STATUS SettingModule(struct CFGstruct *pCFG)
{
RET_STATUS STATUS = RET_SUCCESS;
#ifdef Device_A
pCFG-<ADDH = DEVICE_A_ADDR_H;
pCFG-<ADDL = DEVICE_A_ADDR_L;
#else
pCFG-<ADDH = DEVICE_B_ADDR_H;
pCFG-<ADDL = DEVICE_B_ADDR_L;
#endif
pCFG-<OPTION_bits.trsm_mode =TRSM_FP_MODE;
pCFG-<OPTION_bits.tsmt_pwr = TSMT_PWR_10DB;
STATUS = SleepModeCmd(W_CFG_PWR_DWN_SAVE, (void* )pCFG);
SleepModeCmd(W_RESET_MODULE, NULL);
STATUS = SleepModeCmd(R_CFG, (void* )pCFG);
return STATUS;
}
RET_STATUS ReceiveMsg(uint8_t *pdatabuf, uint8_t *data_len)
{
RET_STATUS STATUS = RET_SUCCESS;
uint8_t idx;
SwitchMode(MODE_0_NORMAL);
*data_len = softSerial.available();
if (*data_len < 0)
{
Serial.print("ReceiveMsg: "); Serial.print(*data_len); Serial.println(" bytes.");
for(idx=0;idx<*data_len;idx++)
*(pdatabuf+idx) = softSerial.read();
for(idx=0;idx<*data_len;idx++)
{
Serial.print(" 0x");
Serial.print(0xFF & *(pdatabuf+idx), HEX); // print as an ASCII-encoded hexadecimal
} Serial.println("");
}
else
{
STATUS = RET_NOT_IMPLEMENT;
}
return STATUS;
}
RET_STATUS SendMsg(float temp,float hum)
{
RET_STATUS STATUS = RET_SUCCESS;
SwitchMode(MODE_0_NORMAL);
if(ReadAUX()!=HIGH)
{
return RET_NOT_IMPLEMENT;
}
delay(10);
if(ReadAUX()!=HIGH)
{
return RET_NOT_IMPLEMENT;
}
union cvt {
float val;
uint8_t b[4];
} temp_bytes,hum_bytes;
temp_bytes.val=temp;
hum_bytes.val=hum;
/*temp_bytes.b[0]=0x11;
temp_bytes.b[1]=0x12;
temp_bytes.b[2]=0x13;
temp_bytes.b[3]=0x14;*/
//TRSM_FP_MODE
//Send format : ADDH ADDL CHAN DATA_0 DATA_1 DATA_2 ...
#ifdef Device_A
uint8_t SendBuf[4] = { DEVICE_B_ADDR_H, DEVICE_B_ADDR_L, 0x17, random(0x00, 0x80)}; //for A
softSerial.write(SendBuf, 4);
#else
//uint8_t SendBuf[4] = { DEVICE_A_ADDR_H, DEVICE_A_ADDR_L, 0x17, random(0x81, 0xFF)}; //for B
//uint8_t SendBuf[8] = { 0xff, 0xff, 17, g_is_wet, temp_bytes.b}; //for B
//uint8_t SendBuf[8] = { 0xff, 0xff, 17, temp_bytes.b}; //for B
uint8_t SendBuf[12] = { 0xff, 0xff, 17, g_is_wet,temp_bytes.b[0], temp_bytes.b[1], temp_bytes.b[2], temp_bytes.b[3],
hum_bytes.b[0], hum_bytes.b[1], hum_bytes.b[2], hum_bytes.b[3]}; //for A
softSerial.write(SendBuf, 12);
#endif
return STATUS;
}
bool jjled=LOW;
void blinkLED()
{
static bool LedStatus = LOW;
digitalWrite(LED_BUILTIN, LedStatus);
LedStatus = !LedStatus;
if(jjled==LOW)
jjled=HIGH;
else
jjled=LOW;
digitalWrite(greenLED, jjled);
}
void setup(){
pinMode(rainPin, INPUT);
pinMode(greenLED, OUTPUT);
pinMode(redLED, OUTPUT);
digitalWrite(greenLED, HIGH);
digitalWrite(redLED, HIGH);
RET_STATUS STATUS = RET_SUCCESS;
struct CFGstruct CFG;
struct MVerstruct MVer;
pinMode(M0_PIN, OUTPUT);
pinMode(M1_PIN, OUTPUT);
pinMode(AUX_PIN, INPUT);
pinMode(LED_BUILTIN, OUTPUT);
softSerial.begin(9600);
Serial.begin(9600);
dht.begin();
#ifdef Device_A
Serial.println("[10-A] ");
#else
Serial.println("[10-B] ");
#endif
STATUS = SleepModeCmd(R_CFG, (void* )&CFG);
STATUS = SettingModule(&CFG);
STATUS = SleepModeCmd(R_MODULE_VERSION, (void* )&MVer);
// Mode 0 | normal operation
SwitchMode(MODE_0_NORMAL);
//self-check initialization.
WaitAUX_H();
delay(10);
if(STATUS == RET_SUCCESS)
Serial.println("Setup init OK!!");
}
void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(rainPin);
Serial.print(sensorValue);
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
Serial.print("Humidity: ");
Serial.print(h);
Serial.print(" %\t");
Serial.print("Temperature: ");
Serial.print(t);
Serial.print(" *C ");
if(sensorValue < thresholdValue){
Serial.println(" - It's wet");
g_is_wet=true;
//digitalWrite(greenLED, LOW);
//digitalWrite(redLED, HIGH);
}
else {
Serial.println(" - It's dry");
g_is_wet=false;
//digitalWrite(greenLED, HIGH);
//digitalWrite(redLED, LOW);
}
if(SendMsg(t,h)==RET_SUCCESS)
{
//blinkLED();
//Serial.println(" Blink2");
}
delay(10000);
//Sleepy::loseSomeTime(10000);
}
This code will send through the LoRa device a packet every 5 minutes containing date, temperature, humidity and rain state.
$ git clone https://github.com/juanjo75es/oficloud-sdk-base .
{
"dependencies": {
"@trust/webcrypto": "^0.9.2",
"aes-js": "^3.1.1",
"argon2": "^0.19.3",
"atob": "^2.1.2",
"blob": "0.0.4",
"btoa": "^1.2.1",
"crypto": "^1.0.1",
"dateformat": "^3.0.3",
"form-data": "^2.3.2",
"fs": "0.0.1-security",
"get-random-values": "^1.2.0",
"http": "0.0.0",
"line-reader": "^0.4.0",
"node-dht-sensor": "0.0.34",
"node-forge": "^0.7.6",
"querystring": "^0.2.0",
"rpi-gpio": "^2.1.2",
"rsa-pem-to-jwk": "^1.1.3",
"secrets.js": "^0.1.8",
"serialport": "^7.1.0",
"sha1": "^1.1.1",
"streamifier": "^0.1.1",
"xmlhttprequest": "^1.8.0"
}
}
$ npm install
var oficloud = require('./oficloud');
var SerialPort = require('serialport');
var gpio = require('rpi-gpio')
var gpiop = gpio.promise;
var dateFormat = require('dateformat');
function setMode(mode)
{
var m0=1;
var m1=1;
switch(mode)
{
case 0://normal
m0=0;
m1=0;
break;
case 1://wake-up
m0=0;
m1=1;
break;
case 2://power-saving
m0=1;
m1=0;
break;
case 3://sleep
m0=1;
m1=1;
break;
}
try{
gpiop.write(11, m0)
gpiop.write(13, m1)
}
catch(err)
{
console.log('Error: ', err.toString())
}
}
function init(ondone)
{
gpiop.setup(11, gpio.DIR_OUT)
.then(() => {
gpiop.setup(13, gpio.DIR_OUT)
.then(() => {
ondone();
})
.catch((err) => {
console.log('Error: ', err.toString())
})
})
.catch((err) => {
console.log('Error: ', err.toString())
})
}
var serialPort = new SerialPort("/dev/ttyUSB0", {
baudRate: 9600,
parser: new SerialPort.parsers.Readline("\n")
});
oficloud.login('myuser@gmail.com','MYPASSWORD',function(res){
console.log("login res: "+res.res);
if(res.e==-1)
return;
function onmsg(msg)
{
//Process received messages here
}
oficloud.open_channel('p',onmsg,function(err){
if(typeof err !="undefined")
{
console.log("open channel error: "+err);
return;
}
//Do your stuff here once you are logged in and joined a channel
//(...)
init(function (){
setMode(3);
var buf = new Buffer.from([ 0xC0, 0x05, 0x01, 0x1A, 17, 44 ]);
//var buf = new Buffer([ 0xC3, 0xC3,0xC3 ]);
serialPort.write(buf);
setMode(0);
});
})
});
function data2float(data,pos)
{
var d=data.slice(pos,pos+4);
var res=new Float32Array(new Uint8Array(d).buffer)[0];
return res;
}
var g_data=[];
var g_data_length=0;
serialPort.on('data', function(data){
console.log(data);
console.log("size: "+data.length);
for(var i=0;i<data.length;i++)
{
g_data[g_data_length]=data[i];
g_data_length++;
if(g_data_length==9)
{
try{
var temperature=data2float(g_data,1);
var humidity=data2float(g_data,5);
var state="dry";
if(g_data[0])
state="wet";
var today0 = new Date();
var today=dateFormat(today0, "yyyy-mm-dd H:MM:ss");
var s="{\"date\":\""+today+"\",\"state\":\""+state+"\",\"temperature\":"+temperature.toFixed(1)+",\"humidity\":"+humidity.toFixed(1)+"}";
console.log(s);
oficloud.send_post('p',s,function(){
})
}
catch(e){console.log(e.toString())}
}
}
});
$ sudo node main.js
Your program will post a message to the 'MYCHANNEL' channel every 5 minutes reporting temperature, humidity and rain state.