Nassim BettachPlaymo 101
Published © Apache-2.0

Smart Mirror | Plug-in Sigfox

Here is a new project about how to build a smart mirror and how to create a plugin for your device from Sigfox backend.

AdvancedFull instructions provided24 hours2,456

Things used in this project

Hardware components

Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
Monitor 24
×1
Lumber
×1
Tinted film
×1
Plexiglass sheet
×1
Blackout Fabric
×1

Software apps and online services

Sigfox Backend
Sigfox Build Sens'it
MagicMirror

Story

Read more

Code

Module.js

JavaScript
Javascript
Module.register("sigfox",{
	decodePayload: function(payload) {
		var payload,
			binary = '',
			ret = [],
			parsedData = [],
			obj = {};

		for (var i = 0; i <= (payload.length - 2); i = i + 2) {
			var byte = parseInt(payload.slice(i, i + 2), 16).toString(2);
			while (byte.length < 8) {
				byte = '0' + byte;
			}
			binary = binary.concat(byte);
		}

		let temperature_MSB = parseInt(binary.slice(0, 8), 2);
		let temperature_LSB = parseInt(binary.slice(8, 16), 2);
		let humidity = parseInt(binary.slice(16, 24), 2);
		let battery = parseInt(binary.slice(24, 29), 2);
		let alarm = parseInt(binary.slice(29, 30), 2);

		let battery = (battery * 0.05) + 2.7;
		if (battery >= 4.15) 
			pourcent = 100;
		else if (battery < 4.15  && battery >= 3.8) 
			pourcent = (battery - 3.275) * 114;
		else if (battery < 3.8 && battery >= 3.6) 
			pourcent = (battery - 3.56) * 250;
		else if (battery < 3.6 && battery >= 3) 
			pourcent = (battery - 3) * 16;
		else 
			pourcent = 0;

		let obj = {};
		obj.key = 'alert';
		obj.value = 1;
		obj.type = '';
		obj.unit = '';
		parsedData.push(obj);

		let obj = {};
		obj.key = 'alarm';
		obj.value = alarm;
		obj.type = '';
		obj.unit = '';
		parsedData.push(obj);

		let obj = {};
		obj.key = 'temperature';
		obj.value = (((temperature_MSB << 8) + temperature_LSB) - 200) / 8;
		obj.type = '';
		obj.unit = '';
		parsedData.push(obj);

		let obj = {};
		obj.key = 'humidity';
		obj.value = humidity / 2;
		obj.type = '';
		obj.unit = '';
		parsedData.push(obj);

		let obj = {};
		obj.key = 'battery';
		obj.value = pourcent;
		obj.type = '';
		obj.unit = '';
		parsedData.push(obj);


		return parsedData;
	},

	getScripts: function() {
		return [
			'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js',
		]
	},

	getStyles: function() {
		return [
			'sigfox.css',
			'https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css',
		]
	},

	getTemplate: function () {
		return "sigfox.njk"
	},

	updateDom: function(data) {
		data[2]['value'] = parseFloat(data[2]['value']);
		data[3]['value'] = parseFloat(data[3]['value']);
		data[4]['value'] = parseFloat(data[4]['value']);
		document.getElementById("temperature").innerText = data[2]['value'] + "°C";
		let tc = document.getElementById("temp_chart");
		tc.style.strokeDasharray = data[2]['value'] / 2 + " 999";
		let color = null;
		let temp = data[2]['value'];
		if (temp <= 24)
			color = 'green';
		else if (temp > 24 && temp <= 26)
			color = 'orange';
		else
			color = 'red';
		if (data[1].value === 1) {
			$.ajax({
				dataType: "json",
				url: 'http://127.0.0.1:5000/buzz',
				success: (data) => {}
			}
		}	
		tc.style.stroke = color;
		document.getElementById("temperature").style.color = color;
		document.getElementById("humidity").innerText = data[3]['value'] + "%";
		let hc = document.getElementById("hum_chart");
		hc.style.strokeDasharray = data[3]['value'] / 2 + " 999";
		document.getElementById("battery").innerText = data[4]['value'] + "%";
		if (data[4]['value'] < 10)
			document.getElementById("battery").style.color = 'red';
		document.getElementById("img_battery").src = 'https://img.icons8.com/color/48/000000/low-battery.png';
	},

	start: function() {
		let self = this;
		let decoded = null;

		setInterval(function() {
			$.ajax({
				dataType: "json",
				url: 'http://127.0.0.1:5000',
				success: (data) => {
					self.updateDom(self.decodePayload(data.data));
				}
			});
		}, 5000);
	},
});

New Feature

C/C++
Here our program to load into the Sens'it to check temperature each 15min and send an alert if the temperature increase or send temperature and humidity each 1 hour
/*!******************************************************************
 * \file main_TEMPERATURE.c
 * \brief Sens'it Discovery mode Temperature demonstration code
 * \author Sens'it Team
 * \copyright Copyright (c) 2018 Sigfox, All Rights Reserved.
 *
 * For more information on this firmware, see temperature.md.
 *******************************************************************/
/******* INCLUDES **************************************************/
#include "sensit_types.h"
#include "sensit_api.h"
#include "error.h"
#include "button.h"
#include "battery.h"
#include "radio_api.h"
#include "hts221.h"
#include "discovery.h"

/******** DEFINES **************************************************/
#define MEASUREMENT_PERIOD					900 /* Measurement  period, in second */
#define ALERT_DELTA							1 /* Alert treshold in degree celcius */
#define ALERT_THRESHOLD						24 /* Temperature in degree celcius */
/******* GLOBAL VARIABLES ******************************************/
u8 firmware_version[] = "TEMP_v2.0.0";


/*******************************************************************/

bool	check_temperature(s16 temperature, s16 prev_temp)
{
	s8		delta;
	u8		n_temp;
	u8		n_prev;

	n_temp = (((temperature + 200) & 0xFFF) - 200) / 8;

	n_prev = (((prev_temp + 200) & 0xFFF) - 200) / 8;

	delta = n_temp - n_prev;
	if ((delta >= ALERT_DELTA) || (n_temp > ALERT_THRESHOLD))
		return (1);
	return (0);
}

int main()
{
    error_t err;
    button_e btn;
    bool send = FALSE;
	s16 prev_temp;
	u8 payload[4] = {0,0,0,0};
	u8 count = 0;
	bool alert = 0;

    /* Discovery payload variable */
    discovery_data_s data = {0};

    /* Start of initialization */

    /* Configure button */
    SENSIT_API_configure_button(INTERRUPT_BOTH_EGDE);

    /* Initialize Sens'it radio */
    err = RADIO_API_init();
    ERROR_parser(err);

    /* Initialize temperature & humidity sensor */
    err = HTS221_init();
    ERROR_parser(err);
	HTS221_measure(&(data.temperature), &(data.humidity));
	prev_temp = data.temperature;

    /* Initialize RTC alarm timer */
    SENSIT_API_set_rtc_alarm(MEASUREMENT_PERIOD);

    /* Clear pending interrupt */
    pending_interrupt = 0;

    /* End of initialization */

    while (TRUE)
    {
        /* Execution loop */

        /* Check of battery level */
        BATTERY_handler(&(data.battery));

        /* RTC alarm interrupt handler */
        if ((pending_interrupt & INTERRUPT_MASK_RTC) == INTERRUPT_MASK_RTC)
		{
			SENSIT_API_set_rgb_led(RGB_BLUE);
			SENSIT_API_wait(30);
			SENSIT_API_set_rgb_led(RGB_OFF);

			count++;
            /* Do a temperatue & relative humidity measurement */
            err = HTS221_measure(&(data.temperature), &(data.humidity));
            if (err != HTS221_ERR_NONE)
            {
                ERROR_parser(err);
            }
            else if (check_temperature(prev_temp, data.temperature) || count == 4)
            {
				count = 0;
                /* Set send flag */
                send = TRUE;
            }
			prev_temp = data.temperature;
            /* Clear interrupt */
            pending_interrupt &= ~INTERRUPT_MASK_RTC;
        }

        /* Button interrupt handler */
        if ((pending_interrupt & INTERRUPT_MASK_BUTTON) == INTERRUPT_MASK_BUTTON)
        {
            /* Count number of presses */
            btn = BUTTON_handler();

            if (btn == BUTTON_FOUR_PRESSES)
            {
                /* Reset the device */
                SENSIT_API_reset();
            }

            /* Clear interrupt */
            pending_interrupt &= ~INTERRUPT_MASK_BUTTON;
        }

        /* Check if we need to send a message */
        if (send == TRUE)
        {
			payload[0] = (data.temperature + 200) >> 8;
			payload[1] = data.temperature + 200;
			payload[2] = data.humidity;
			payload[3] = ((data.battery & 0x1F) << 3) + (alert << 3);

            /* Send the message */
            err = RADIO_API_send_message(RGB_GREEN, payload, 4, FALSE, NULL);
            /* Parse the error code */
            ERROR_parser(err);

            /* Clear button flag */
            data.button = FALSE;

            /* Clear send flag */
            send = FALSE;

			alert = 0;
			payload[0] = 0;
			payload[1] = 0;
			payload[2] = 0;
			payload[3] = 0;
        }

        /* Reed switch interrupt handler */
        if ((pending_interrupt & INTERRUPT_MASK_REED_SWITCH) == INTERRUPT_MASK_REED_SWITCH)
        {
            /* Clear interrupt */
            pending_interrupt &= ~INTERRUPT_MASK_REED_SWITCH;
        }

        /* Accelerometer interrupt handler */
        if ((pending_interrupt & INTERRUPT_MASK_FXOS8700) == INTERRUPT_MASK_FXOS8700)
        {
            /* Clear interrupt */
            pending_interrupt &= ~INTERRUPT_MASK_FXOS8700;
        }

        /* Check if all interrupt have been clear */
        if (pending_interrupt == 0)
        {
            /* Wait for Interrupt */
            SENSIT_API_sleep(FALSE);
        }
    } /* End of while */
}
/*******************************************************************/

Backend

Python
Python / Flask
from flask import Flask, json
from flask_cors import CORS
import requests
from gpiozero import Buzzer
from time import sleep

creds = {
    "login": "xxxx",
    "password": "xxxx"
}

app = Flask(__name__)
CORS(app)
b = Buzzer(4)

@app.route('/')
def hello_world():
    messages = requests.get('https://backend.sigfox.com/api/devices/86BB17/messages', auth=(creds['login'], creds['password'])).json()
    if len(messages):
        message = messages['data'][1]
    else:
        message = {}
    return app.response_class(
	response=json.dumps(message),
	status=200,
	mimetype='application/json'
    )

@app.route('/buzz')
def buzz():
    for i in range(5):
        b.on()
        sleep(1)
        b.off()
        sleep(1)
    return app.response_class(
	response=json.dumps({}),
	status=200,
	mimetype='application/json'
    )


if __name__ == '__main__':
    app.run()

Module.njk

HTML
NJK template
<div class="flex flex-col w-full h-full justify-center items-center mt-12">
    <div class="flex flex-row self-center w-full h-auto justify-center items-center mb-12">
        <h3 class="font-hairline">Salle serveur du 101</h3>
    </div>
    <div class="flex flex-row self-center w-full h-auto justify-center items-center">
        <svg class="circle-chart" viewbox="0 0 33.83098862 33.83098862" width="200" height="200" xmlns="http://www.w3.org/2000/svg">
            <circle class="circle-chart-background" stroke="#000000" stroke-width="2" fill="none" cx="16.91549431" cy="16.91549431" r="15.91549431" />
            <circle class="circle-chart-circle" id="temp_chart" stroke="red"  stroke-width="2" stroke-dasharray="0 999" fill="none" cx="16.91549431" cy="16.91549431" r="15.91549431"/>
        </svg>
        <p class="absolute text-lg font-hairline"><span id="temperature">Loading ...</span></p>
    </div>
</div>

<div class="flex flex-col w-full h-full justify-center items-center mt-4">
    <div class="flex flex-row self-center w-full h-auto justify-center items-center">
        <svg class="circle-chart" viewbox="0 0 33.83098862 33.83098862" width="200" height="200" xmlns="http://www.w3.org/2000/svg">
            <circle class="circle-chart-background" stroke="#000000" stroke-width="2" fill="none" cx="16.91549431" cy="16.91549431" r="15.91549431" />
            <circle class="circle-chart-circle" id="hum_chart" stroke="blue"  stroke-width="2" stroke-dasharray="0 999" fill="none" cx="16.91549431" cy="16.91549431" r="15.91549431"/>
        </svg>
        <p class="absolute text-lg font-hairline"><span id="humidity">Loading ...</span></p>
    </div>
</div>

<div class="flex flex-col w-full h-full justify-center items-center mt-4">
    <div class="flex flex-row self-center w-full h-auto justify-center items-center">
        <img src="https://img.icons8.com/color/48/000000/high-battery.png" id="img_battery">
        <span id="battery">Loading ...</span>
    </div>
</div>

Credits

Nassim Bettach

Nassim Bettach

3 projects • 5 followers
Playmo 101

Playmo 101

1 project • 1 follower

Comments