IOT at you service with Lora and Ruby
Manage your devices under the Lora Network
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:
- TLS protocol with Loriot (No longer supported)
- Http push with Resiot (No longer supported)
- MQTT with A2aSmartCity
- 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 ...
Downlink: send data to the device
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.(eui: 'Insert here your device eui')
To explicitly delete enqueued messages:
lora.(eui: 'Insert here your device eui')
or passing the id to delete ony that message:
lora.(eui: 'Insert here your device eui', id: 1234)
Uplink: receive data from devices
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
- Fork it
- Create your feature branch (
git checkout -b my-feature
) - Commit your changes (
git commit -am 'I made extensive use of all my creativity'
) - Push to the branch (
git push origin my-feature
) - 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