I have XBee Gateway ZB, Wifi and I'm experimenting with the Digi Device API. I already tried the Heroku App xbeegateway.herokuapp.com and it works really good.
So I wanted to access online the Datastream. Using Google Spreadsheet or something like this. So reading the documentation from digi. I could acces the Gateway through http://devicecloud.digi.com/ws/DataStream/00000000-00000000-00######-########/xbee.serialIn/
But that's not the problem. I thought that I could see the serialIn
that I send via Arduino to the Gateway. (I'm sending a string which contains the temperature of my room.) But looking at the xml I can't really understand it.
<result>
<resultSize>1</resultSize>
<requestedSize>1000</requestedSize>
<pageCursor>94a9cb44-1-cb91b9c6</pageCursor>
<DataStream>
<cstId>9921</cstId>
<streamId>
00000000-00000000-00######-########/xbee.serialIn/[00:##:##:00:##:##:##:##]!
</streamId>
<dataType>STRING</dataType>
<forwardTo/>
<currentValue>
<id>230fee58-158b-11e5-a4a7-fa163eefb35b</id>
<timestamp>1434612303755</timestamp>
<timestampISO>2015-06-18T07:25:03.755Z</timestampISO>
<serverTimestamp>1434612304781</serverTimestamp>
<serverTimestampISO>2015-06-18T07:25:04.781Z</serverTimestampISO>
<data>TWVzc3VuZzogMjIgQ2Vsc2l1cw0K</data>
<description/>
<quality>0</quality>
</currentValue>
<description/>
<units/>
<dataTtl>2678400</dataTtl>
<rollupTtl>2678400</rollupTtl>
</DataStream>
</result>
I see the Timestamp but I can't understand what this should mean <data>TWVzc3VuZzogMjIgQ2Vsc2l1cw0K</data>
. Is it kind of character code or did I misunderstand something?
If I look in JavaScript in Heroku App at the Serial Terminal Widget I get this code. It looks like it's using var decoded = utils.base64_decode(newData);
.
/*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) 2014 Digi International Inc., All Rights Reserved.
*/
'use strict';
angular.module('XBeeGatewayApp')
//jshint -W098
.controller('serialWidgetCtrl', function ($scope, utils, $log, dashboardApi, notificationService) {
$scope.data_sending = false;
$scope.last_received_timestamp = undefined;
$scope.getStreamUpdateHandler = function (newData) {
// For the serial widget, data *should* come in as an object of the form
// {"content": "...", "format": "base64"}
var _newData = newData.value;
if (_.isEmpty(_newData)) {
$log.warn("Serial widget got malformed data", newData);
return;
}
var _timestamp = newData.timestamp;
newData = _newData;
// XBee Gateway
if (angular.isString(newData)) {
var decoded = utils.base64_decode(newData);
if (_.isEmpty(decoded)) {
// Might not be decoded.
newText = newData;
} else {
// Successfully decoded the data.
newText = decoded;
}
$log.info("Adding text:", newText);
$scope.displaySerialText(newText, true);
$scope.last_received_timestamp = _timestamp;
return;
}
// Or, XBee Wi-Fi
else if (newData.content !== undefined){
// Check format to see if we should b64 decode
var newText;
if(newData.format === "base64"){
newText = utils.base64_decode(newData.content);
} else {
newText = newData.content;
}
$scope.displaySerialText(newText, true);
$scope.last_received_timestamp = _timestamp;
} else {
// TODO display some error?
$log.warn('Serial widget got malformed data', newData);
}
}
$scope.setStreamUpdateHandler = function (newData) {
// Updates to the set stream could be handled here too, as we use the same for get/set
return;
}
$scope.serialEnterKeypress = function($event){
if($scope.serialOutText){
$scope.sendText($scope.serialOutText);
}
$event.preventDefault();
}
$scope.sendText = function(text) {
$scope.data_sending = true;
if ($scope.widget.add_carriage_returns) {
// Insert a CR before/after string to make it show on new line
// on both ends
var cr = String.fromCharCode(13);
text = cr + text + cr;
}
dashboardApi.send_serial($scope.widget.device, $scope.widget.radio, text).then(
function(result){
// On success, show the sent text
$scope.displaySerialText(text, false);
// Clear the input box for next entry
$scope.serialOutText = null;
// Reenable input
$scope.data_sending = false;
},
function(reason){
notificationService.error("Error sending text. Please try again.");
// Reenable input
$scope.data_sending = false;
});
}
$scope.$watch('data_sending', function (sending) {
if (sending) {
$scope.widgetState = 1;
} else {
$scope.widgetState = 0;
}
});
})
.directive('serialWidget', function (widgetRegistry, utils, dataStreams, $log) {
// called after DOM element is compiled
var linker = function postLink(scope, element) {
scope.$element = element;
var type = 'serial';
var spec = widgetRegistry.get(type);
// See http://lodash.com/docs#bind
// (dataUpdate simply calls scope.updateHandler)
var getCallback = _.bind(scope.getStreamUpdateHandler, scope);
var setCallback = _.bind(scope.setStreamUpdateHandler, scope);
utils.postlinkWidget(type, scope, element, spec, getCallback, setCallback);
// Any more fancy footwork can be done here.
// Manually listen for serialIn
var device = scope.widget.device;
var inputStream = "xbee.serialIn/[" + scope.widget.radio + "]!";
$log.debug("Listening for serial stream: ", device, inputStream);
var removeListener = dataStreams.listen(device, inputStream, scope.getStreamUpdateHandler);
scope.$on('$destroy', function () {
removeListener();
});
// Widget display area
// Use jquery to find, Angular's jqlite doesn't support selector
var output_pane = $(element).find(".serial-display");
scope.displaySerialText = function(text, isInbound){
var $newText = null;
if(isInbound){
$newText = $('<span/>').addClass("serial-in");
// Show incomming text inline
// Split string to handle any Carriage Returns
if(text === "\r"){
$newText.append($('<br>'));
} else {
var snippets = text.split("\r");
_.each(snippets, function (snippet) {
// CR at start & end will make cause empty strings
if(snippet === ""){
$newText.append($('<br>'));
} else {
$newText.append($('<span/>').text(snippet));
}
});
}
} else {
// Outgoing should be in it's own line
$newText = $('<p/>').text(text).addClass("serial-out");
}
$(output_pane).append($newText);
$(output_pane).scrollTop($(output_pane)[0].scrollHeight);
}
};
// AngularJS directive setup
return {
templateUrl: "widgets/serialWidget/serialWidget.tpl.html",
restrict: 'AE',
link: linker,
controller: 'serialWidgetCtrl',
scope: { widget: "=serialWidget", widgetState: "=state" }
};
})
// This function, referred to in AngularJS as a "run block", is called by
// AngularJS after the injector is created and is used to bootstrap the
// application. The XBee ZigBee Cloud Kit makes use of the run block
// to add widget definitions to the widget registry at start-up.
.run(function(widgetRegistry) {
// Adding the widget to the widget registry
var widget_type_key = 'serial';
var widget_description = 'Serial Data Widget';
var widget_spec = {
// Whether or not the widget is built-in or user-created
// (i.e., whether the code is in /src/app or /src/common)
builtin: true,
// widget size: X,Y (columns, rows)
size: [3, 2],
// description appearing in 'Widget Type' list when adding new
// widgets
description: widget_description,
directive: "serial-widget",
// camel-case version of directive
directive_c: "serialWidget",
// properties pertaining to widget settings
/*
has_input: does the widget's data get updated from Device Cloud?
sends_output: does the widget send data to the device?
input_xform: can the user specify a custom transformation to apply
to incoming data? (optional)
options: list of objects defining the settings associated with this
widget type
- options generally follow the Revalidator API
(http://github.com/flatiron/revalidator)
*/
has_input: false,
sends_output: false,
options: [
{key: "add_carriage_returns", label: "Add Carriage Returns",
type: "boolean", required: false, 'default': true}
]
};
// DO NOT CHANGE ANY CODE BELOW HERE.
widgetRegistry.put(widget_type_key, widget_spec);
});
Okay my bad. Never heard of base64. Maybe because I'm new to online data streaming.
After searching I found a site that use base64 decode: https://www.base64decode.org
I could decode
TWVzc3VuZzogMjIgQ2Vsc2l1cw0K
.