• Time to read 3 minutes
Zoe Helper: A Sigfox SOS Device - Part 2

Zoe Helper: A Sigfox SOS Device - Part 2

In this part of the project, I process the message sent by Sigfox Backend via callback. To recap, this is the message:

http://jacksonng.org/sigfox/newnotifications.php?message={data}&deviceid={device}

The job of newnotifications.php is to read the message, check who it is intended for, and send the message to the receiver's mobile device. 

Whose message is this?

To find out who we should be sending this message to, I use a database table to track the Sigfox device ID as well as the email address of the receiver. In an actual scenario, I would envision that the person who bought my Zoe Helper device registers it through a product website and enter both his unique device ID as well as his email address and contact details. But here, we will just save them into the database table. Here's the database structure.

CREATE TABLE `device` ( `deviceid` varchar(6) NOT NULL, `email` text NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Configuring global.php and library functions for sending notifications and decoding Sigfox messages

I use OneSignal.com's Notification service to send messages to the intended mobile device (the mobile app will be discussed in part 3). To use this, register yourself on OneSignal.com. Follow the instructions for Web Push to setup your account and receive your OneSignal App ID and REST ID.

Edit global.php to configure your database server address, username, password and database name. Enter your OneSignal App ID and REST ID as well.

//global.php

const server = "<your database server address>";
const dbuser = "<your database server username>";
const dbpw = "<your database server password>";
const db = "<your database name>";
const global_app_id = '<your onesignal app id>';
const global_rest_id = '<your onesignal rest id';

The codes to send a push notification are as follows:

//global.php

//send onesignal notification to mobile user
function sendMessage($email, $message, $arrayfull){
	$content = array(
		"en" => $message
	);
		
	$fields = array(
		'app_id' => global_app_id,
		'filters' => $arrayfull,
		'data' => array("foo" => "bar"),
		'contents' => $content
	);
		
	$fields = json_encode($fields);
		
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, "https://onesignal.com/api/v1/notifications");
	curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json; charset=utf-8','Authorization: Basic '. global_rest_id));
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
	curl_setopt($ch, CURLOPT_HEADER, FALSE);
	curl_setopt($ch, CURLOPT_POST, TRUE);
	curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

	$response = curl_exec($ch);
	curl_close($ch);
		
	return $response;
}

Reference: https://documentation.onesignal.com/v3.0/reference

The message from Sigfox backend needs to be decoded to turn it back into the same form that was first sent from the Zoe Helper Device. The follow codes do that:

//global.php

//This is a translation of UnaBiz's structuredmessage JavaScript code to PHP
//Source: https://github.com/UnaBiz/sigfox-gcloud/tree/master/decodeStructuredMessage
function decodeLetter($code) {
    //  Convert the 5-bit code to a letter.
    if ($code == 0){ 
        return 0; 
    }

    if ($code >= firstLetter && $code < firstDigit)
    {
        return ($code - firstLetter) + JS_charCodeAt('a', 0);
    }
  
    if ($code >= firstDigit){ 
        return ($code - firstDigit) + JS_charCodeAt('0',0);
    }
    return 0;
}

function decodeText($encodedText0) { /* eslint-disable no-bitwise, operator-assignment */
    //  Decode a text string with packed 5-bit letters.
    $encodedText = $encodedText0;
  
    $text = [0, 0, 0];
    for ($j = 0; $j < 3; $j = $j + 1) {
        $code = $encodedText & 31;
        $ch = decodeLetter($code);

        if ($ch > 0) $text[2 - $j] = $ch;
        $encodedText = $encodedText >> 5;
    }

    if ($text[2]){
        $result = str_fromcharcode($text[0], $text[1], $text[2]);
    }
    elseif ($text[1]){
        $result = str_fromcharcode($text[0], $text[1]);
    }
    else{
        $result = str_fromcharcode($text[0], $text[1]);
    }

    return $result;
} 

function decodeMessage($data, $textFields) {
    
  if (!$data) return null;

    $result = null;
    
    for ($i = 0; $i < strlen($data); $i = $i + 8) {
      $name = substr($data, $i, $i + 4);
      $val = substr($data, $i + 4, $i + 8);
      $encodedName =
        (intval($name[2], 16) << 12) +
        (intval($name[3], 16) << 8) +
        (intval($name[0], 16) << 4) +
        intval($name[1], 16);
      $encodedVal =
        (intval($val[2], 16) << 12) +
        (intval($val[3], 16) << 8) +
        (intval($val[0], 16) << 4) +
        intval($val[1], 16);

      //  Decode name.
      $decodedName = decodeText($encodedName);
      if ($textFields && strpos_array($textFields, $decodedName) >= 0) {
        //  Decode the text field.
        $result[$decodedName] = decodeText($encodedVal);
      } else {
        //  Decode the number.
        $result[$decodedName] = $encodedVal / 10.0;
      }
    }
    return $result;
} 

These set of codes were written by UnaBiz in JavaScript. I converted it to PHP so that I can run on my virtual server that runs PHP and not Node.JS. You could find the original codes in JavaScript here

Refer to my Github repository for these codes.

Sending Notifications

The following are the PHP codes to send the notification to the recipient by referencing the recipient's email address stored as a Tag field in OneSignal.

<?PHP

    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
    header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');
    header("Content-Type: application/json; charset=UTF-8");
    error_reporting(E_ERROR);

	include("global.php");

    //search the database for the email address based on sigfox equipment ID
    $conn = new mysqli(server, dbuser, dbpw, db);
    $deviceid = $_GET['deviceid'];
    $result = $conn->query("select email from device where deviceid = '" . $deviceid . "'");

    while($rs = $result->fetch_array(MYSQLI_ASSOC)) {
	    $outp = $rs["email"];
    }

    $conn->close();

    $result = decodeMessage($_GET['message'], ['d1', 'd2', 'd3']);

	$email = $outp;
	$message = implode(" ",$result); 
        
	$arrayfull = [];

	$arrayhead = array("field" => "tag", "key" => "email", "relation" => "=");
	$emailArray = explode(',', $email);

	if (count($emailArray) == 1) {
		$arrNew = array('value' => $emailArray[0]);
		$arrayhead = $arrayhead + $arrNew;
		array_push($arrayfull, $arrayhead);
	}
	else {
		foreach ($emailArray as &$value) {
			$arrOr = array('operator' => 'OR');
			$arrNew = $arrayhead + array("value" => $value);
			array_push($arrayfull, $arrayNew);
			array_push($arrayfull, $arrOr);
		}
	}

	$response = sendMessage($email, $message, $arrayfull);
	$return["allresponses"] = $response;
	$return = json_encode( $return);

	//find the end of the recipient string in the response
	$pos = strpos($return, '\"recipients\":');
	$posend = $pos + 15;

	$lastchunk = substr($return, $posend, strlen($return)-$posend+1);
	$commapos = strpos($lastchunk, ",");
	$bracketpos = strpos($lastchunk, "}");

	if ($commapos != null){
		$num = substr($lastchunk, 0, $commapos);
	}
	else {
		$num = substr($lastchunk, 0, $bracketpos);
	}

	$json_out = "[" . json_encode(array("result"=>(int)$num)) . "]";
	echo $json_out;
?>

Find these codes on Github here.

What's Next?

Part 3 of the project discusses the implementation of a Cordova mobile app that receives the notification and displays it on a mobile device.

Photo by Felipe Faria on Unsplash