-
-
Save jonathanjuursema/f7c892067e10c05894678b8e531c72e6 to your computer and use it in GitHub Desktop.
Ensketon TTN Bridge
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Required packages. | |
var ttn = require("ttn"); | |
var request = require('request'); | |
// Global variables. | |
var appID = "appId"; // The App ID from TTN | |
var accessKey = "accessKey"; // The Access Key for that app | |
var hostname = "hostname"; // The hostname and port for the database server API. | |
var alreadyRegistered = []; // A list of already registered device ID's. | |
// Authenticate with TTN | |
ttn.data(appID, accessKey) | |
.then(function (client) { | |
// Display a message if connection was successfull. | |
client.on("connect", function () { | |
console.log("Connected to TTN.") | |
}); | |
// Register a handler to fire when an uplink message is received. | |
client.on("uplink", function (devID, payload) { | |
console.log("Received uplink from", devID); | |
// Dispatch the payload for analysis and further action. | |
postToServer(devID, payload); | |
}); | |
}) | |
// If something goes wrong, display what went wrong. | |
.catch(function (error) { | |
console.log("[ error ]", error.toString()); | |
}); | |
// The logic for bridging an uplink message to the database server. | |
function postToServer(devID, payload) { | |
// Convert hexadecimal device UID to integer. | |
devID = "2" + String(parseInt("0x" + devID)); | |
// Unpack the payload. Analyse what kind of payload it is, and extract values. | |
// Stores values in the 'payload' variable. | |
payload = unpackPayload(payload); | |
// If the payload type is not recognized, display to payload text. | |
if (payload[0] == null) { | |
console.log("[ error ] Unknown message:", payload[1].payload_raw.toString()); | |
return; | |
} else if (payload[0] == "location") { | |
if (alreadyRegistered.indexOf(devID) == -1) { | |
console.log("[ new dev ] New device:", devID) | |
alreadyRegistered.push(devID) | |
var createPayload = payload.slice(); | |
createPayload[0] = "register"; | |
makeRequest(devID, createPayload); | |
} | |
} | |
// Dispatch the API request. | |
makeRequest(devID, payload); | |
} | |
// The logic for unpacking the payload. | |
function unpackPayload(payload) { | |
// Match sensor measurements. Example: 74 31 39 2E 33 31 68 37 36 2E 33 36 77 30 2E 32 35 | |
var values_regex = /t([0-9\-\.]+)h([0-9\-\.]+)w([0-9\-\.]+)/; | |
// See if the payload text matches the format for a measurement message. | |
// This also already fills the 'values' variable with the matched measurements. | |
// Gotta love them regexes. | |
var values = payload.payload_raw.toString().match(values_regex); | |
if (values != null) { | |
// Extract the time so we can reformat it to the right format. | |
var time_regex = /([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:.*)/; | |
var time = payload.metadata.time.match(time_regex); | |
var time_str = time[1] + "-" + time[2] + "-" + time[3] + ":" + time[4] + ":" + time[5] + ":" + time[6]; | |
// Display all extracted values. | |
console.log("[ measure ] Temp:", parseFloat(values[1]), "Hum:", parseFloat(values[2]), "Wind:", parseFloat(values[3]), "Time:", time_str); | |
// Return the measurements and indicate the type of message. | |
return ["measurement", parseFloat(values[1]), parseFloat(values[2]), parseFloat(values[3]), time_str]; | |
} | |
// Match location. Example: 4C 35 32 2E 32 33 39 36 6C 36 2E 38 35 37 30 | |
var values_regex = /L([0-9\-\.]+)l([0-9\-\.]+)/; | |
// This is mostly the same as the sensor measurement. See above for comments. | |
var values = payload.payload_raw.toString().match(values_regex); | |
if (values != null) { | |
var time_regex = /([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:.*)/; | |
var time = payload.metadata.time.match(time_regex); | |
var time_str = time[1] + "-" + time[2] + "-" + time[3] + ":" + time[4] + ":" + time[5] + ":" + time[6]; | |
console.log("[ location ] Latitude:", parseFloat(values[1]), "Longtitude:", parseFloat(values[2]), "Time:", time_str); | |
return ["location", parseFloat(values[1]), parseFloat(values[2])]; | |
} | |
// Unknown message. | |
return [null, payload]; | |
} | |
// The logic for constructing the URL based on the values. | |
function constructUrl(deviceID, values) { | |
// We have multiple types of URL, the first value is the payload type. | |
switch (values[0]) { | |
// Construct the measurement URL. Replace dots with commas. | |
case "measurement": | |
return "http://" + hostname + "/prod/registermeasurement/" + deviceID + "/2" + | |
"/3/" + String(values[1]).replace(".", ",") + | |
"/4/" + String(values[2]).replace(".", ",") + | |
"/5/" + String(values[3]).replace(".", ",") + | |
"/" + values[4]; | |
// Construct the location URL. Replace dots with commas. | |
case "location": | |
return "http://" + hostname + "/prod/updatelocation/" + deviceID + "/2" + | |
"/" + String(values[1]).replace(".", ",") + | |
"/" + String(values[2]).replace(".", ",") + | |
"/100"; | |
// Construct new device URL. Replace dots with commas. | |
case "register": | |
return "http://" + hostname + "/prod/registersensorsystem/" + deviceID + "/2" + | |
"/" + String(values[1]).replace(".", ",") + | |
"/" + String(values[2]).replace(".", ",") + | |
"/100"; | |
} | |
} | |
// The logic for making a web request. | |
function makeRequest(devID, payload) { | |
// If payload unpacking was successfull, construct the URL based on the values extracted from the payload. | |
var url = constructUrl(devID, payload); | |
console.log("[ http/get ]", url); | |
// POST the values to the database server. | |
request.post(url, function (error, response, body) { | |
// If something goes wrong, let us know. Also let us know when it succeeds. | |
console.log("[ http/res ]", (error !== null ? error.toString() : response.statusCode)); | |
console.log("Request to database server complete."); | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "ensketon-bridge", | |
"version": "0.0.0", | |
"private": true, | |
"scripts": { | |
"start": "node ./app.js" | |
}, | |
"dependencies": { | |
"ttn": "~2.3.1", | |
"request": "~2.81.0" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment