Sensor Values to Cloud IoT Service

From SecuriWiki
Jump to: navigation, search


I would say this is a moderate to difficult modification, depending on your Linux knowledge. Also, I make no guarantees that this won't brick your Almond+, and offer no warranties that it won't burn down your house, so follow at your own risk.

To upload sensor data to a cloud database, here is one way to do it. In this example I use ThingSpeak, but it shouldn't be hard to make it work for others. First, sign up here (it's free), and then setup a channel here. For the channel all I did was change the name and enter the two field names. For most of mine I just used 'State' and 'Battery'. Next, still in the channel definition, click on the API Keys tab and then click on 'Generate New Write API Key'. Copy this key, you will need it soon. Now you are ready to start sending data to the channel. In this example we will be sending two values, state and battery level, as mentioned previously. It should not be too hard to modify it to add more fields.

On your Almond+, login via SSH, and do the following:

touch /sbin/load_thingspeak.pl
chmod 755 /sbin/load_thingspeak.pl

Then edit the script (I am in no way a coder, so please be kind with the code below):

vi /sbin/load_thingspeak.pl

Paste the following into the script:

#! /usr/bin/perl -w

use warnings;
use strict;
use XML::Simple;
use LWP::UserAgent;

my $ua = LWP::UserAgent->new;
my $xml = new XML::Simple;
my $data = $xml->XMLin('/DeviceList.xml', ForceArray => [qw( LastKnownValue )]);
foreach my $device(@{$data->{Device}}) {
	#print $device->{Name},"\n";
	my $type = $device->{DeviceType};
	my $batt_level=0;
	my $state=$device->{ValueVariables}->{LastKnownValue}[0]{content};
	my $state_str="";
	if ($type eq "ContactSwitch") {
		if ($state eq "false") {
			$state_str = "0"; #CLOSED
		} else {
			$state_str = "1"; #OPEN
		}
		if ($device->{ValueVariables}->{LastKnownValue}[1]{content} eq 'false') {
			$batt_level=100;
		} else {
			$batt_level=25;
		}
	} elsif ($type eq "DoorSensor") {
		if ($state eq "false") {
			$state_str = "0"; #CLOSED
		} else {
			$state_str = "1"; #OPEN
		}
		$batt_level=$device->{ValueVariables}->{LastKnownValue}[1]{content};
	} elsif ($type eq "DoorLock") {
		$state_str = $state;
		$batt_level=$device->{ValueVariables}->{LastKnownValue}[2]{content};
	} elsif ($type eq "SmokeDetector") {
		$state_str = $state;
		$batt_level=$device->{ValueVariables}->{LastKnownValue}[1];
	} elsif ($type eq "MovementSensor") {
		if ($state eq "false") {
			$state_str = "0"; #No movement
		} else {
			$state_str = "1"; #Movement
		}
		$batt_level=$device->{ValueVariables}->{LastKnownValue}[1]{content};
	} elsif ($type eq "SmartACSwitch") {
		if ($state eq "false") {
			$state_str = "0"; #OFF
		} else {
			$state_str = "1"; #ON
		}
		#$state_str = $state;
		$batt_level=(hex($device->{ValueVariables}->{LastKnownValue}[10]{content}) * hex($device->{ValueVariables}->{LastKnownValue}[3]{content})) / hex($device->{ValueVariables}->{LastKnownValue}[4]{content}); # this converts F to C for temperature
	} elsif ($type eq "Thermostat") {
		$state = $device->{ValueVariables}->{LastKnownValue}[1]{content};
		if ($state eq "Off") {
			$state_str = "0"; #OFF
		} else {
			$state_str = "1"; #ON
		}
		#$state_str = $state;
		$batt_level=($device->{ValueVariables}->{LastKnownValue}[0]{content} -32) * (5/9);
	}
	my $resp="";
	my $dev_key=GetTSKey($device->{ID});
	#print "$device->{Name} key: $dev_key state: $state_str batt: $batt_level\n";
	if ($dev_key ne "0") { $resp = SendToTS("$dev_key","$state_str","$batt_level") }
	#print "resp: $resp\n";
}

sub SendToTS
{
	# Get parameters
	my ($key,$field1,$field2) = @_;
	# Remove the newline....
	chomp $key;
	chomp $field1;
	chomp $field2;
	#print "input: key $key field1 $field1 field2 $field2 \n";

	my $post="http://api.thingspeak.com/update?key=" . $key ."&field1=" . $field1 . "&field2=" . $field2;
	#print "post: $post \n";
	# Create a request
	my $req = HTTP::Request->new(POST => $post);
	# Pass request to the user agent and get a response back
	my $res = $ua->request($req);

	# Check the outcome of the response
	if ($res->is_success) {
		return $res->content, "\n";
	} else {
		return $res->status_line, "\n";
	}
}

sub GetTSKey {
	my $case = $_[0];
	my $ret=0;
	if ($case eq "1")  { $ret= "0" } # Replace the 0 with your Write API Key from your channel
	if ($case eq "2")  { $ret= "0" }
	if ($case eq "3")  { $ret= "0" }
	if ($case eq "4")  { $ret= "0" }
	if ($case eq "5")  { $ret= "0" }
	if ($case eq "6")  { $ret= "0" }
	if ($case eq "7")  { $ret= "0" }
	if ($case eq "8")  { $ret= "0" }
	if ($case eq "9")  { $ret= "0" }
	if ($case eq "10") { $ret= "0" }
	if ($case eq "11") { $ret= "0" }
	return $ret;
}


To make the script work, you will have to first go to the GetTSKey subroutine and add your API Key for the device you are interested in logging. I have 11 devices currently, so there are 11 keys in the sub. To know what your device numbers are, become familiar with the file /DeviceList.xml. All of your device definitions and their status are in this file. It's not terribly complicated, and the script should be fairly obvious in what it's doing. Just match up the Device ID to the API Key.

Save the script. Don't modify the /DeviceList.xml. Next you need to have a method of calling the script periodically. There are two methods that I have found. The right way is to (if you are on R066 firmware) fix the cron setup using this link, and then add something like this:

 crontab -e
 * * * * *       /sbin/load_thingspeak.pl

This will run the script every minute. You can obviously choose whatever interval you like. The hacky way is to use an existing process. I originally did it the hacky way by adding a call to the script above in the /sbin/check_connection.sh script, at the end just after the 'sleep 60' line. This method is not recommended, and could get broken by a firmware upgrade.

That's pretty much it.