Skip to content

Node/Device API for sending and receiving data over LoRa or WiFi/Ethernet

Notifications You must be signed in to change notification settings

i4things/NodeAPI

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NodeAPI

*THE API ALSO HAS A WIFI VERSION WITH THE SAME DEFINITIONS AND FUNCTIONALITY IN CASE YOU WANT TO BENEFIT DIRECTLY FROM ESP32 OR ESP8266 BOARD WIFI OR SIM800L GPRS WITHOUT USING LoRa GATEWAY

**ULP (Ultra Low Power) EXAMPLES ALSO AVAILABLE FOR WIFI API AND LORA API in /examples/ESP32/ULP

*** Sonoff/ESP8266-01 1,2,4 RELAYY EXAMPLES IN : /examples/ESP8266-01

#FREQUENCY

The officially supported are : 433Mhz, 868Mhz and 915Mhz - default is 868Mhz To switch to another one please pass the requencyRange parameter (supported options are : IoTThing_433, IoTThing_868, IoTThing_915) On the gateway side you also need to choose a appropriate frequency:

For nodes on 433 Gateway can be : 433.1, 433.3, 433.5

For nodes on 868 Gateway can be : 868.1, 868.3, 868.5

For nodes on 915 Gateway can be : 915.1, 915.3, 915.5

example : IoTThing thing(CS_PIN, INT_PIN, RST_PIN, key, thing_id, NULL, IoTThing_433);

#LOGGING

To enable logging inside the library you need to add at the begging of you main source fail/sketch:

///////////////////////////////////////////////////////////////////////////////////

// LOG constants

#define LOG64_ENABLED

#include <Log64.h>

and also make sure somewhere inside the "setup" function you have: Serial.begin(115200);

#Node/Device/Thing Data Send/Receive API

On the node/thing side i4things provide one simple straight forward class that can be used to deliver data to the authority/server.

class IoTThing
{
  public:
    // constructor
    IoTThing(uint8_t slaveSelectPin_,
             uint8_t interruptPin_,
             uint8_t resetPin_,

             uint8_t key_[16],
             uint64_t id_,
             void (* receive_callback_)(uint8_t buf_[], uint8_t size_, int16_t rssi_) = NULL,
              uint8_t requencyRange = IoTThing_868,
             uint64_t gateway_id_ = 10); // default open gateway id

    // call before using
    void init();

    // set id ( in case after start needs to be changed)
    void set_id(uint64_t id_ );

    // set key ( in case after start needs to be changed)
    void set_key(uint8_t key_[16]);

    // set gateway id ( in case after start needs to be changed)
    void set_gateway_id(uint64_t gateway_id_ );

    // register to receive callback after the message has been acknowledged from the gateway
    void register_ack(void (* ack_callback_)(int16_t rssi_));

    // register to receive callback after the message has failed/ in msec
    void register_timeout(void (* timeout_callback_)(uint16_t timeout_));

    // return signal strength in %
    uint8_t signal_strength();

    // return total messages sent
    uint32_t total_messages();

    //return total acknowledged messages send
    uint32_t ack_messages();

    //return received messages count
    uint32_t recv_messages();

    //return total retransmit messages send
    uint32_t retransmit_messages()

    //add discrete data
    //use this if you want for example to store decimal(floating-point) temperature between -20 and +60 in one byte
    // pos is the position from which the data will be written
    // e.g. : add_discrete(buf, 0,  -20.0, 60.0, 23.65, 1);
    //you need to ensure that you at least have container_size bytes available in the buffer
    //1 <= conainer_size <= 4
    //on return pos will have the value of first free byte(next postion after the data)
    static void add_discrete(uint8_t buf_[], uint8_t &pos_, double min_, double max_, double value_, uint8_t container_size_);

    //read discrete value
    static double get_discrete(uint8_t buf_[], uint8_t &pos_, double min_, double max_, uint8_t container_size_);

    //add unsigned integer data up to 62bit ( 0 - 4611686018427387904) - the container size will be adjusted automatically depending on the value
    // pos is the position from which the data will be written
    //you need to ensure that you have at least 8 bytes available in the buffer - as this is the maximum bytes that the number can occupy in worst case scenario
    //on return pos will have the value of first free byte(next postion after the data)
    static void add_uint(uint8_t buf_[], uint8_t &pos_, uint64_t value_);

    // get unsigned integer value
   static uint64_t get_uint(uint8_t buf_[], uint8_t &pos_)

    // return false if a error is logged
    // max message size in bytes is 31
    bool send(uint8_t buf_[], uint8_t size_);

    // return true - if all previous tasks has been completed and ready to accept new data
    bool is_ready();

    // call if you want to stop retry's to send the message
     void cancel();

    // return true - if last message sending has hit timeout and failed
    bool timeout_hit();

    // please call in the main loop to be able to dispatch data and menage logic
    void work();
};

Example how to use the API:

#include "IoTThing.h"


// exmaple PINS for Feather 32u4
#define CS_PIN 8
#define RST_PIN 4
#define INT_PIN 7

#define DEVICE_ID 1000
uint8_t key[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

// you can also optionally pass:
// - function pointer/address for callback messages when received
// - gateway id - in case you want to use a private/closed gateway
//
// example for message received callback:
//
// called when packet received from node
// void received(uint8_t buf_[], uint8_t size_, int16_t rssi)
// {
//  //process message in buf_[] here with length = size_ 
// };
//
// IoTThing thing(CS_PIN, INT_PIN, RST_PIN, key, DEVICE_ID, received);
//

IoTThing thing(CS_PIN, INT_PIN, RST_PIN, key, DEVICE_ID);



// 2 minutes
#define MESSAGE_INTERVAL  120000
uint32_t MESSAGE_LAST_SEND;

void setup() {
  MESSAGE_LAST_SEND = millis() + MESSAGE_INTERVAL * 2;

  thing.init();
}



void loop() {
  // let IoT layer do it job
  thing.work();

  // try send message every 2 min
  if (((uint32_t)(((uint32_t)millis()) - MESSAGE_LAST_SEND))  >= MESSAGE_INTERVAL) {
    MESSAGE_LAST_SEND = millis();

    uint8_t msg[2];

    msg[0] = 22; // e.g. temperature 
    msg[1] = 78; // e.g. humidity

    // check if IoT layer is ready to accept new message
    if (thing.is_ready()) {
      thing.send(msg, 2);
    }
    else {
      // cancel previous work and send new message
      thing.cancel();
      thing.send(msg, 2);
    }
  }

}

IMPORTANT

For the simple open source node library you will also need to download and make available the RadioHead library : RadioHead.zip - the provided one has a bugfix on ESP32 platform.

PRACTICALITY AND EFFICIENCY

In the API there are found very helpful static functions related to easy and efficient way of representing data in byte buffer(byte array) message :

static void add_discrete(uint8_t buf_[], uint8_t &pos_, double min_, double max_, double value_, uint8_t container_size_);
static double get_discrete(uint8_t buf_[], uint8_t &pos_, double min_, double max_, uint8_t container_size_);

static void add_uint(uint8_t buf_[], uint8_t &pos_, uint64_t value_);
static uint64_t get_uint(uint8_t buf_[], uint8_t &pos_);

Using the add/get discrete you can add to the buffer and read from the buffer discrete values - e.g. if you want to store temperate in the interval between -20C and +60C efficiently in only one byte but still having better resolution then 1/2 degree then you can use the discrete functions in the following fashion:

uint8_t buf[2];
uint8_t buf_pos = 0;

double temp1 = 23.5;
double temp2 = 18.1;

//insert into the buffer
IoTThing::add_discrete(buf, buf_pos,-20.0, 60.0, temp1, 1);
IoTThing::add_discrete(buf, buf_pos,-20.0, 60.0, temp2, 1);

buf_pos = 0;

double temp1_read_from_buffer = IoTThing::get_discrete(buf,buf_pos, -20.0, 60.0, 1);
double temp2_read_from_buffer = IoTThing::get_discrete(buf,buf_pos, -20.0, 60.0, 1);

Using the add/get uint you can add to the buffer and read from the buffer positive integer values between 0 and 4611686018427387904. The value will be stored in the minimum possible bytes e.g. if the value can fit in one byte it will occupy only one byte, if it can fit in 2 bytes it will occupy only two bytes and etc. up to 8 bytes. This way you can save space in the message, be efficient and optimize your traffic:

uint8_t buf[8]; // make sure we have space for maximum size
uint8_t buf_pos = 0;

//insert into the buffer
IoTThing::add_uint(buf, buf_pos, 11); // will occupy only 1 byte
IoTThing::add_uint(buf, buf_pos, 500); // will occupy only 2 bytes

buf_pos = 0;
int val1 = (int)IoTThing::get_uint(buf, buf_pos);
int val2 = (int)IoTThing::get_uint(buf, buf_pos);