IOT at you service with Lora and Ruby

Manage your devices under the Lora Network

Version     Travis CI   Quality

What is Lora?

LoRaWAN is a Low Power Wide Area Network with features that support low-cost, mobile, and secure bi-directional communication for Internet of Things (IoT), machine-to-machine (M2M), and smart city, and industrial applications. LoRaWAN is optimized for low power consumption and is designed to support large networks with millions and millions of devices. Innovative features of LoRaWAN include support for redundant operation, geolocation, low-cost, and low-power - devices can even run on energy harvesting technologies enabling the mobility and ease of use of Internet of Things.

Install

gem install lora-rb

To use it in a bundle, add to gem file gem 'lora-rb' and run bundle install

Connection to lora server

To use this gem you have to choose your provider. Each of them has its own features and offers a bouquet of protocols among which you can choose to connect to the lora network.

This version supports:

  1. TLS protocol with Loriot (No longer supported)
  2. Http push with Resiot (No longer supported)
  3. MQTT with A2aSmartCity
  4. MQTT with Fractalgarden (Those who are developing this gem)

Configuration

Insert private informations in the yaml:

lib/config/private.yml

and the configuration's parameters in the config file.

lib/config/config.rb

If you are using rails you can use the generator:

bundle exec rails generate lora_rb:install

Every provider assign you a token and an app id. Someone can provide some additional parameters such as username and password. Insert your secret data in the yaml:

defaults:
  appid:
  token:
  # Some provider may use it
  username:
  password:
  connector_id:

If you are using Rails 5.1 you can encrypt these private data and insert into the yaml with erb. Something like this:

defaults: &defaults
  appid: <%= Rails.application.secrets.lora_appid %>
  token: <%= Rails.application.secrets.lora_token %>
  # etc ...

Connection's protocols

While the information in the yaml file is subdivided by environment, the connection configuration file contains shared parameters for all environments.

TLS (with Loriot)

LoraRb.configure do |config|
  config.protocol = :tls
  config.host = 'eu1.loriot.io'
  config.port = 737
end

HTTP (with Resiot)

LoraRb.configure do |config|
  config.protocol = :http
  config.host = 'eu72.resiot.io'
  config.port = 80
end

RABBITMQ

LoraRb.configure do |config|
  config.protocol = :rabbitmq
  config.host = 'localhost'
end

MQTT (with A2a)

Insert in uplink_url the topic url where to wait the device messages. Insert in downlink_url the topic url where publish the message to send to the device. Insert in downlink_response_urls the list of topic urls where the lora network put the response. Use a label as phase's name.

LoraRb.configure do |config|
  config.protocol = :mqtt
  config.host = 'ptnetsuite.a2asmartcity.io'
  config.port = 8883
  config.uplink_url = '/sub/v1/users/{username}/apps/{appid}/devices/+/uplink/+'
  config.downlink_url = '/api/v1/users/{username}/apps/{appid}/devices/{deveui}/downlink/post/reply/{clientid}/id/{requestid}'
  config.timeout = 30.0
  config.downlink_response_urls = {
      queued: 'reply/{clientid}/id/{requestid}',
      transmitted: '/sub/v1/users/{username}/apps/{appid}/devices/{deveui}/events/downlink'
  }
  config.ssl = true
  config.ssl_file = 'ssl_certificates/mqtt_ca.crt'
end

MQTT (with Fractalgarden free lora server)

Currently, the free network server is under development.

LoraRb.configure do |config|
  config.provider = :fractalgarden
  config.protocol = :mqtt
  config.host = 'lora.fractalgarden.com'
  config.port = 1883
  config.uplink_url = 'application/{appid}/node/+/rx'
  config.downlink_url = 'application/{appid}/node/{deveui}/tx'
  config.timeout = 30.0
  config.downlink_response_urls = [
      { name: :queued, url: 'reply/{clientid}/id/{requestid}'},
  ]
  config.ssl = false
end

Usage

From irb require the library:

require 'lora-rb'

then create an instance

lora = LoraClient.new

you should receive the instance with variables depending on protocol:

#<LoraClient:0x0056318ad8e048 ...
lora.send_cmd(eui: 'Insert here your device eui', confirmed: true, data: '0101')

Add debug: true to get a trace of the operations. Use the value :full to get more information. The response depend on the provider. This is an example using tls protocol and the provider Loriot.io:

{ "cmd":"tx", "EUI":"a000000000001234", "port":40, "confirmed":false, "data":"0101" }

or

{ "cmd":"txd", "EUI":"a000000000001234", "seqdn":114, "seqq":113, "ts":1489133179385 }

or

{ "cmd":"tx", "EUI":"a000000000001234", "seqdn": 4, "data": "0301", "success": "Data enqueued" }

This is an example using http protocol and the provider Resiot.io

{
 "CommType"=>"comm_rx",
 "Connector"=>"69643d32",
 "AppEui"=>"a1b2c3d4e5f60001",
 "DevEui"=>"be7a00000000123c",
 "Port"=>"10",
 "GatewayEUIs"=>["00000111180e2222"],
 "Payload"=>"0063",
 "Extra"=>{"JSONRXINFO"=>"[\"{\\\"altitude\\\":0,\\\"latitude\\\":0,\\\"loRaSNR\\\":8.8,\\\"longitude\\\":0,\\\"mac\\\":\\\"00000111180e2222\\\",\\\"name\\\":\\\"00000111180e2222\\\",\\\"rssi\\\":-67}\"]",
           "adr"=>"true", "bandwidth"=>"125", "fCnt"=>"132", "frequency"=>"868100000", "spreadFactor"=>"7"},
 "DT"=>"2017-07-03T15:28:01.098886848+02:00",
 "auth_token"=>"abcd1234",
 "lora"=>{"CommType"=>"comm_rx", "Connector"=>"abcd", "AppEui"=>"abcd", "DevEui"=>"a000000000001234", "Port"=>"10", "GatewayEUIs"=>["abcd"], "Payload"=>"0063",
          "Extra"=>{"JSONRXINFO"=>"[\"{\\\"altitude\\\":0,\\\"latitude\\\":0,\\\"loRaSNR\\\":8.8,\\\"longitude\\\":0,\\\"mac\\\":\\\"00000111180e2222\\\",\\\"name\\\":\\\"00000111180e2222\\\",\\\"rssi\\\":-67}\"]", "adr"=>"true", "bandwidth"=>"125", "fCnt"=>"132", "frequency"=>"868100000", "spreadFactor"=>"7"}, "DT"=>"2017-07-03T15:28:01.098886848+02:00"}
}

This is an example provided using mqtt protocol and the provider A2a:

 {"reply"=>{"confirmed"=>false, "id"=>150, "payload"=>"0100", "port"=>40, "priority"=>0}, "status"=>200, "eui"=>nil, "port"=>nil}

Implemented only on Mqtt

Using MQTT, by default the param wait_response is true. This means after publishing a message it waits the response on dedicated topic.

config.downlink_response_urls = [
  { name: :queued, url: 'reply/{clientid}/id/{requestid}'},
  # { name: :transmitted, url: '/sub/v1/users/{username}/apps/{appid}/devices/{deveui}/events/downlink' }
  # { name: :confirmed, url: nil },
]

For each downlink urls, a subscription is performed. Then, after the publication, a reading is made for each one. If you expect more than one response in the same topic, add it with url=nil to not subscribe it newly

If you dont want to wait the response you just set that param to false.

lora.send_cmd(eui: 'Insert here your device eui',
              data: 'f0a1b3ff',
              confirmed: false,
              wait_response: false)

Only on Mqtt (A2a)

The a2a network server let you to manage the enqueued messages.

The param delete_previous_enqueued_commands is true by default as well. It means previous enqueued messages to the node are deleted (the queue is emptied) before sending a new one.

If you need to keep the queue:

lora.send_cmd(eui: 'Insert here your device eui', confirmed: true, data: '0100', delete_previous_enqueued_commands: false)

To get enqueued messages:

lora.get_enqueued_messages(eui: 'Insert here your device eui')

To explicitly delete enqueued messages:

lora.delete_enqueued_messages(eui: 'Insert here your device eui')

or passing the id to delete ony that message:

lora.delete_enqueued_messages(eui: 'Insert here your device eui', id: 1234)

To listen your devices:

lora.listen

You can also pass a block.

lora.listen do |response|
  # Do something ...
end

or with RabbitMq:

lora.listen do |delivery_info, properties, body|
  # Do something ...
end

or with mqtt protocol:

lora.listen do |topic, response|
  # Do something ...
end

Using http protocol, instead, #listen is not available because uplink messages are received by api.

To receive only one message:

lora.read_data

It returns the same data of listen method.

Close the connection

lora.quit

Compatibility

Ruby 2.3+

Install

gem install lora-rb

To use it in a bundle, add to gem file gem 'lora-rb' and run bundle install

To Do

  • [x] Connect to a device with tls protocol v0.1.0
  • [x] Send data to a device v0.1.0
  • [x] Receive data from a device v0.2.0
  • [x] Organization in modules to support new protocols v0.2.0
  • [x] Add a configuration v0.3.0
  • [x] Add a rails generator v0.3.0
  • [x] Add http push support v0.5.0
  • [x] Add RabbitMq support v0.6.0
  • [x] Add Mqtt support v0.9.0
  • [ ] Test with many devices

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-feature)
  3. Commit your changes (git commit -am 'I made extensive use of all my creativity')
  4. Push to the branch (git push origin my-feature)
  5. Create new Pull Request

Testing

Wide coverage with xx unit tests and xxx assertions

To test locally install the development requirements:

bundle install

Then execute:

bundle exec ruby test/unit_test.rb

Performance tests:

bundle exec ruby test/performance_test.rb

Found a bug?

Please open an issue.

License

The GNU Lesser General Public License, version 3.0 (LGPL-3.0) See LICENSE file