Pic n Mix IoT Data Collection
ThingSpeak is an excellent platform for quick and easy IoT Data Collection in the cloud.
As a test we have our office test Pic n Mix machine linked up and sending it’s vend stats once every 5 minutes to give an example of what can be done.

Pic n Mix World machine at Adventure Island Southend, UK
IoT Data Collection
The machine is configured to run a perl script every 5 minutes to check the latest stats, if the stats don’t change then nothing happens apart from sending a heartbeat message. This allows us to see that the machine is still on and running. The data for this can be seen below.
Heartbeat Data
Updated every 5 minutes when the machine is running (ThingSpeak Channel)
[iframe_loader src=”https://thingspeak.com/apps/plugins/194817″]
Hopper Vend Counts
Updated when a vend happens (ThingSpeak Channel)
[iframe_loader src=”https://thingspeak.com/apps/plugins/177098″]
Hopper Vend Errors
Updated when a vend happens, this shows us which hoppers may be running low (ThingSpeak Channel)
[iframe_loader src=”https://thingspeak.com/apps/plugins/177105″]
Takings (not real money yet!)
Updated when a vend happens (ThingSpeak Channel)
[iframe_loader src=”https://thingspeak.com/apps/plugins/177110″]
Perl Script
The following is a quick and dirty hack that parses data from our vending machine stats application and is executed every 5 minutes as a cron job.
Note that our API_Keys have been removed
#!/usr/bin/perl use Getopt::Std; use Data::Dumper qw(Dumper); $statsCmd="azucastats | grep RAW"; #valid value to show data is valid, returned as first field in RAW data $validValue=43981; #file used to store previous runs data so we only send data if needed $datFile="/var/run/statsreport.dat"; #number of times to retry if invalid valid value is returned from the stats $retryCount=3; #api keys $vendStatsApiKey=""; $hopperStatsApiKey=""; $hopperErrorApiKey=""; $heartbeatApiKey=""; #Vend Stats fields $vsVendCountField="field1"; $vsVendsRemainingField="field2"; $vsEmergancyVendsField="field3"; $vsCashBoxField="field4"; $vsTotalCashField="field5"; $vsScaleFactorField="field6"; #Hopper stats field $hs1Field="field1"; $hs2Field="field2"; $hs3Field="field3"; $hs4Field="field4"; $hs5Field="field5"; $hs6Field="field6"; $hs7Field="field7"; $hs8Field="field8"; #heartbeat fields $hbField="field1"; $hbDeviceStateField="field2"; $hbErrField="field3"; #set to 1 by cmd option -n which will force a send of data to the server $reportNow=0; #base url $baseUrl="https://api.thingspeak.com/update?api_key="; sub printUsage() { print "usage $0 -n\n"; print " -n force data send\n"; exit; } sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s }; #get the command lineoptions my %options=(); getopts ("hn", \%options) or printUsage(); if (defined $options{h}) { printUsage(); } if (defined $options{n}) { $reportNow=1; print "Forcing data send\n"; } elsif ( !-e $datFile ) { $reportNow=1; print "Dat file does not exist, forcing data send\n"; `touch $datFile` or die "Unable to create empty dat file $! "; } @statsFields=(); while ($retryCount-- != 0) { $stats=`$statsCmd`; if ($? == -1 ) { print "Failed to execute: $!\n" } elsif ($? & 127) { print "child died with signal %d, %s coredump\n", ($? & 127), ($? & 128) ? 'with' : 'without'; } else { printf "child exited with value %d\n", $? >> 8; } print "$stats"; @statsFields = split(",", $stats); if ((@statsFields[0] eq "RAW " . $validValue)) { last; } else { print "Warning, invalid raw value: " . @statsFields[0] . "\n"; } } if ($retryCount < 0) { die "Invalid raw value: " . @statsFields[0]; } else { print "Retry was " . $retryCount . "\n"; } #always send heartbeat $url=$baseUrl . $heartbeatApiKey . "&" . $hbField . "=" . trim(@statsFields[1]); $url=$url . "&" . $hbDeviceStateField . "=" . trim(@statsFields[34]); $url=$url . "&" . $hbErrField . "=" . trim(@statsFields[35]); sendUrl($url); if (($reportNow==0) && !sysopen( dataFile, $datFile, O_RDONLY)) { print "Unable to open dat file $datFile\n"; $reportNow=1; } else { #if not forcing stats update then check the existing dat file, exit if #it matches the data just got from stats foreach my $line (<dataFile>) { if ( $line eq $stats ) { print "Data has not updated, exiting now....\n"; exit; } } } close (dataFile); #vend stats first $url=$baseUrl . $vendStatsApiKey . "&"; $url=$url . $vsVendCountField . "=" . trim(@statsFields[1]); $url=$url . "&" . $vsVendsRemainingField . "=" . trim(@statsFields[18]); $url=$url . "&" . $vsEmergancyVendsField . "=" . trim(@statsFields[19]); $url=$url . "&" . $vsCashBoxField . "=" . trim(@statsFields[23]); $url=$url . "&" . $vsTotalCashField . "=" . trim(@statsFields[22]); $url=$url . "&" . $vsScaleFactorField . "=" . trim(@statsFields[33]); sendUrl($url); #hoper counts $url=$baseUrl . $hopperStatsApiKey . "&"; $url = $url . $hs1Field . "=" . trim(@statsFields[2]); $url = $url . "&" . $hs2Field . "=" . trim(@statsFields[4]); $url = $url . "&" . $hs3Field . "=" . trim(@statsFields[6]); $url = $url . "&" . $hs4Field . "=" . trim(@statsFields[8]); $url = $url . "&" . $hs5Field . "=" . trim(@statsFields[10]); $url = $url . "&" . $hs6Field . "=" . trim(@statsFields[12]); $url = $url . "&" . $hs7Field . "=" . trim(@statsFields[14]); $url = $url . "&" . $hs8Field . "=" . trim(@statsFields[16]); sendUrl($url); #hopper errors $url=$baseUrl . $hopperErrorApiKey . "&"; $url = $url . $hs1Field . "=" . trim(@statsFields[3]); $url = $url . "&" . $hs2Field . "=" . trim(@statsFields[5]); $url = $url . "&" . $hs3Field . "=" . trim(@statsFields[7]); $url = $url . "&" . $hs4Field . "=" . trim(@statsFields[9]); $url = $url . "&" . $hs5Field . "=" . trim(@statsFields[11]); $url = $url . "&" . $hs6Field . "=" . trim(@statsFields[13]); $url = $url . "&" . $hs7Field . "=" . trim(@statsFields[15]); $url = $url . "&" . $hs8Field . "=" . trim(@statsFields[17]); sendUrl($url); #if we are here dump the data to the data file open( dataFile, ">", $datFile) or die "Unable to open dat file for save $datFile $!"; print dataFile $stats; close (dataFile); exit(0); #sends the url and checks for non 0 return, dies if it is 0 sub sendUrl() { my $url = shift; $dataState = `wget -q -O- \"$url\"` or die "Error sending $url : " . $dataState; print "url ret=" . $dataState . "\n"; if ($dataState == 0) { die "Error sending $url : " . $dataState; } }