Create a connected PIR sensor with SigFox

Pir sensor
Pir sensor

In a previous blog post I described how I built a PIR sensor connected to Internet, based on the use of a Raspberry Pi device. As you can read in this previous post, the solution have a certain number of complexities like requiring a power supply, a 3G modem …

As it was originally planed to be done, here is the design of the same product, based on a SigFox device. This is really interesting to demonstrate the differences and the advantages of this technology. Let’s review how to built it !

 

Let’s take a look to the hardware part

SigFox TD1208 as a connected PIR sensor
SigFox TD1208 as a connected PIR sensor

The first thing to notice on the picture is the power supply provided by two batteries. Then the simplicity of the design have also to be noticed : 1 chip, 1 antenna and a couple of wires. The price of the design is as a consequence lower and the reliability higher.

Let’s have a look to the software part

The software part is different, as the platform does not run something like a Linux OS, developing with SigFox means developing with a micro-controller, like for an arduino. The TD1208 chip is containing a ARM Cortex M3 processor and it is possible to upload a home built firmware. To create the PIR function, here is the code I customized from the modem example :

As for Arduino, the first thing is to do the setup. The first thing is to create an Interrupt callback on the PIR events, the second thing is to create a Sched to proceed the transmission. This method sounded like the more easy to do in the existing code

#define PIR_PORT    USR0_PORT                /**< PIR port */
#define PIR_BIT     USR0_BIT                 /**< PIR bit */
#define PIR_MASK    USR0_MASK                /**< PIR mask */
void TD_USER_Setup(void)
{
   // ...

   // *** My custom code
   AT_printf("Startup!");
   int type;
   IRQn_Type irq_parity;

   // Configure the PIR wire as an input
   GPIO_PinModeSet(PIR_PORT, PIR_BIT, gpioModeInputPull, 1);
   // Configure an interrupt on Pire wire
   type = (PIR_MASK & TD_GPIO_ODD_MASK) ?
           TD_GPIO_USER_ODD : TD_GPIO_USER_EVEN;
   TD_GPIO_SetCallback(type, PIRInterrupt, PIR_MASK);
   // Enable rising & falling edge interrupts on button pin
   GPIO_IntConfig(PIR_PORT, PIR_BIT, true, true, true);

   // Clear and enable the corresponding interrupt in the CPU's Nested Vector
   // Interrupt Controller
   irq_parity =
               (PIR_MASK & TD_GPIO_ODD_MASK) ? GPIO_ODD_IRQn : GPIO_EVEN_IRQn;
   NVIC_ClearPendingIRQ(irq_parity);
   NVIC_EnableIRQ(irq_parity);


    // Initialize the timer to generate IRQs
    // temps passé en seconde (premier arg ou tick de 1/32k
    myCycleCounter = 14;
    myTimer = TD_SCHEDULER_AppendIrq(20, 0 , 0, TD_SCHEDULER_INFINITE, mySched, 0);
}

The next part concerns the Interrupt callback: The objective is to detect and store if the PIR has been activated during the sched sleep period.

static void PIRInterrupt(uint32_t mask)
{
    if (GPIO_PinInGet(PIR_PORT, PIR_BIT) == 0) {
      if ( PIRstate == PIR_ST_UNKNOWN )
         PIRstate = PIR_ST_NOTACTIV;
    } else {
        PIRstate = PIR_ST_ACTIV;
    }
    PIRevent = true;
}

Now here is the sched code : This function is automatically called after 20 seconds, but we are processing data only every 15 iterations to limit a transmit rate every 5 minutes. This is a small sleep time in fact as we are limited to 140 frame sent per days, so normally the stand-by time should be about 10 minutes. One interesting thing to consider also is that the time reference is not reliable and in my test, shorter than expected. So I would recommend a sleep period from 12 to 15 minutes to ensure the message rate limit.

static void mySched(uint32_t arg, uint8_t repetition)
{
    if ( myCycleCounter == 15 ) {
        myCycleCounter = 0;

        unsigned int temp = TD_MEASURE_VoltageTemperatureExtended(true);
        unsigned int volt = TD_MEASURE_VoltageTemperatureExtended(false);
        unsigned int lum = 4096 - getLuminosity(6);
        uint8_t message[12];
        message[0] = (PIRstate==PIR_ST_ACTIV)?0x01:0x00;
        message[1] = 0x00;
        message[2] = ( temp >> 8 ) & 0xff;
        message[3] = ( temp ) & 0xff;
        message[4] = ( volt >> 8 ) & 0xff;
        message[5] = ( volt ) & 0xff;
        message[6] = ( lum >> 8 ) & 0xff;
        message[7] = ( lum ) & 0xff;
        message[8] = 0x00;
        message[9] = 0x00;
        message[10] = 0x00;
        message[11] = 0x00;
        sprintf(_smess,"AT$SS=%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
                      message[0], message[1], message[2], message[3],
                      message[4], message[5], message[6], message[7],
                      message[8], message[9], message[10], message[11]);
        AT_printf("%s",_smess);
        sendMess = 0;
        if ( PIRevent ) {
            sendMess = 1;
        } else {
            if ( PIRlastSent != -1 && PIRlastSent != PIRstate ) {
                sendMess = 1;
            }
            // 30 minutes - force a message update to get temperature ...
            if ( myLongCycleCounter >= 6 ) {
                myLongCycleCounter = 0;
                sendMess = 1;
            }
            myLongCycleCounter++;
        }
        if ( sendMess == 1 ) {
               myLongCycleCounter = 0;
               PIRlastSent = PIRstate;
        }

        PIRevent = false;
        PIRstate = PIR_ST_NOTACTIV;
    } 
    myCycleCounter++;
}

Normally, this procedure should send the message over the SigFox network. But unfortunately the call to the ad-hoc procedure is failing and reseting the device. For this reason I had to write some bad code in the main loop …

void TD_USER_Loop(void)
{
    int c;
    // ...
    if (sendMess == 1) {
       GPIO_PinOutSet(LED_PORT,LED_BIT);
       int i;
       for ( i = 0 ; i < strlen(_smess) ;i++) {
           AT_Parse((int)_smess[i]);
       }
       GPIO_PinOutClear(LED_PORT,LED_BIT);
       sendMess = 0;
    }
}

This is not optimal but works… I’m sure we can do better by removing the sched and proceed in the main loop but it needed some investigation on loop period I did not had time to do.. By-the-way, this is to illustrate how to do a such project.

To finish, now let’s have a look to the back-end

As described in some previous post, the back-end is a simple php page receiving a callback from the SigFox network with the message sent as parameter. The message has to be extract to get back the values :

<html>
  <head>
   <title>Demonstrateur SigFox</title>
  </head>
  <body>
  <?php 
     $_data = $_GET["data"];
     $_avgSignal = $_GET["avgSignal"];
     $_presence = intval($_data[0] . $_data[1], 16);
     $_temp = intval($_data[4] . $_data[5] . $_data[6] . $_data[7], 16);
     $_volt = intval($_data[8] . $_data[9] . $_data[10] . $_data[11], 16);
     $_lum = intval($_data[12] . $_data[13] . $_data[14] . $_data[15], 16);
  ?>
  </body>
</html>

To conclude

In my point of view the main point to retain is that to build a PIR solution based on a SigFox device is a little bit more complicated, because you need to solder some wires and write a real peace of code to make it working. But basically this is our job to do that. And compared to the RPI solution, this is a real industrial solution with an affordable cost.

 

This entry was posted in IoT, Sigfox and tagged , . Bookmark the permalink.

20 Responses to Create a connected PIR sensor with SigFox

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.