CaliPile TPiS1S138 Presence and Motion Detector - Library Port Help


#21

@bpr My conclusion is that yes it works and it’s reliable.

With the current firmware settings and only messing with the Motion and Presence sensitivity settings, I get get a reliable 1 to 1.5-meter pickup range without setting the sensitivity so high you get constant false alarms.

I tried a few Fresenial covers over the sensor from the Panasonic PIR sensors I bought and they didn’t really help much but I’m sure I didn’t have them setup optimally either.

If your read the evaluation paper for this sensor which is linked at the top of the code they show how you can tweak the sensor and use rings to cover the sensor to get better range or more directional sensing areas.

They say they were able to achieve 3-8 meter detection range and I believe they did but you’re going to have to change more than the sensitivity settings to achieve that range with reliable operation.

If you’re covering a hallway that’s less than a meter wide then it will work just fine as is, just play with the sensitivity until it’s acting right. Some more tweaking would be needed to detect movement in a room larger than 5-foot square.

Maybe detecting movement from the ceiling pointing down would work good also.

The low power consumption can’t be beaten. I haven’t verified this personally but I trust the resellers 11uA idle current consumption claim.

I just want to detect the presence of people when they are in front of a product so we can turn on the 5 inch LCD screen automatically and turn it off when nobody is around to view it to save on battery life.

Let me know how it goes for you.


#22

@RWB,
I’ve had a little time to check out the calipile a bit more, and it does seem to have a lot more potential for accuracy but for the life of me, I can’t yet wrap my head around the practical difference between “motion” and “presence,” even after reading the stuff from Excelitas and some other sources on the web. Some materials I read state that presence is supposed to relate to smaller movement and motion relates to larger movement, and that detecting presence tells you something warm is moving slightly and detecting motion tells you something warm is moving more than slightly. Excelitas seems to have a more complex perspective that includes over-and-under temperature sensing, with temperature relating to the warm something either moving away or moving toward the sensor. And the sensor “remembers” the temperature obtained at some point. And you can fiddle with the cutoff over time for some purposes. Am I understanding this? The general idea is you can fine tune accurate sensing a bit more with all those variables that with just, for example, a stupid PIR?
With the sensor I bought from Tindie I have to set CALIPILE_TP_PRES_THLD to 0xF0 and CALIPILE_TP_MOT_THLD to 0xFE to avoid spurious sensing. Is that similar what you’re finding? Are these variables the same as “counts” and of what metric are “counts” exactly? Also, I don’t see where the “cutoffs” mentioned by Excelitas are set in calipile.ino, either. Probably don’t need to have it all figured out for a lot of applications, I guess.
I also haven’t even touched the temperature thresholds, wouldn’t know how to do that. It’s a deceptively complex little sensor!
The other thing I seem to be seeing is that the sensor is very sensitive to reflections - it will sense motion behind it if it has certain surfaces close by in front of it and I move behind it. Do you see that? Is that possible?
Whew!
Edit: OK, I have been able to use the vastly lower 0x32 “counts” settings now without significant false detections. Not sure why the change. Still want to understand better the difference between motion and presence, and how to use temperature threshold settings. I like this sensor, thanks @RWB for introducing it to the forum!


#23

@bpr I have not had as much time to play with the sensor due to my current workload but I did play with it enough to know the current code and the bare sensor breakout without any lenses or directing tubes you get about 1.5 meters of detection range.

I added a few lenses but they did not extend the sensing range, they were not ideal lenses though so that test means nothing.

I did leave the sensor setup in the Kitchen so I see what it picks up while I walk by it.

Currently, my settings are like this, which are not perfect but I don’t see any false alarms with these settings. I’m still testing all this and that’s why I wanted to know what your experience has been.

I think the best way to extend the range of the sensor is to add a focusing can like shown below, or just buy that version that has the can on already on it. That’s if you want the longer range.

I don’t use the thermal shock alert function since I have no need for it but I understand it just triggers the alert once the preset temp is reached.

The last settings I have for motion and presence thresholds are 0x32 which is 50 out of 255. With that setting, I don’t see any false alarms, and I get more presence detection than movement detection in the range of about 1 meter from the sensor. Considering I just need to detect the presence of a person in front of a LCD screen I think I may just end up using only the presence detection feature and forget about the Motion setting for that particular application.

Did you change any settings other than the 2 above?

I do want to use it for security also so ideally I would like to see it cover more range if possibe. I can see it working great in a hallway or from the ceiling pointing down towards the floor.

Just need to keep testing and we should be able to end up with that low power always on Motion sensor we have been wanting :slight_smile:

And about the refections, the sensor has no problem picking up heat refected or not so that does not surprise me at all.


#24

Hey guys,

I just stumbled across this site through google. I can give you some limited support since I’m the guy from this video: https://youtu.be/PjIX5PPt5Ng ;).

If you are having a hard time, why do you not use the website to ask us. Let’s see what I can do for you here. Unfortunately I’m not allowed to upload pdf’s, so I cannot share too much here. In general we have a demokit (also available through distributors) which comes with all the technical notes. But certainly you should be able to reach more than 2 meters also without the kit.

So 2 important notes: A bare sensor is indoors safe to use with thresholds above 30 counts at a LP2 setting of 8s and LP1 setting of less or equal 1 (see the video for details). If you put it behind a thin plastic cover such as PP or HD-PE you loose signal but are also less prone to ambient influences.
You can improve the sensors response with a small piece of code, which I simply paste in here.

 // Exemplary c-code for the CaliPile
// Presence detection with host optimization
// The code does 
// - improve the adoption speed of the CaliPile
// - determine the presence of a person

// After start-up of the host system
// The person must leave and enter the field-of-view once
// for a proper operation
// Once a (thermal) instability was recognized
// by heat-up or cool-down of the sensor
// during presence sensing, 
// the host system will switch to safe mode, where
// presence sensing is not possible any more
// this will be indicated by a blinking of the LED

// the code was optimized for the Excelitas Demonstration Set
// in case of a fixed mounting of the bare sensor
// please adapt all settings like filters and thresholds
// to your application conditions 

// this code must be adapted for each host system
// Excelitas is not liable for the code
// All rights belong to Excelitas but the code
// can be used and modified for any CaliPile application
// free of charge

// SMBus/I2C Rx Tx Buffer for register + eeprom content
unsigned char           SMB_buf[64];		 
// SMBUS Slave address = default 10            
unsigned char           slave_address;	                

// timer flag is on to trigger once the host optimization procedure. 
// This will lead to a faster resetting of the sensor at the power-on.
// on power-on you MUST write this configuration to initialize the 
// sensor properly for the host optimization procedure
// thresholds, filter settings etc. must be optimized in the application
// every application is unique and required detailed understanding and/or testing 
//unsigned char   register_write[32] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
//	0x8D,0x0D,15,30,30,0x09,0x04,20,0xFF,0x00,0,0}; // high sensitivity
unsigned char   register_write[32] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0x8B,0x0B,30,30,30,0x09,0x04,20,0xFF,0x00,0,0}; // low sensitivity

// 32 bit variables
unsigned long int TPobject;  
unsigned long int TPambient; 

// presence detection requires a stable environment
// once a person is in the field of view there is no way
// to distinguish between background and signal with
// a 1 channel sensor. 
// Thus a stability requirement is needed for safe
// absence detection.
unsigned long int TPambient_was = 0; 

unsigned long int TPObjLP1;  
unsigned long int TPObjLP2; 
unsigned long int TPambLP3;  

// those are states of the CaliPile
// used for presence detection or recognition
typedef enum Tintstatus { 
	STATnointerrupt=0, STATinterrupt, STATabsence, 
	STATpresence, STATresetting, STATsetting} intstatus;
#define MAXSTATUS         5

intstatus  current_status = STATnointerrupt;

// return value: (0) = Transmission OK
//               (1) = SA+W not acked  
//               (2) = start address not acked
//               (3) = SA+R not acked    
// parameters  : start adress = 0...31
//               length = no of bytes to read 1...32
char read_register(char start_adr, char length)
{
    char status;
	// here comes controller specific code for I2C communication
    return(status);
}   
              
// return value: (0) = Transmission OK
//               (1) = SA not acked  
//               (2) = start address not acked
//               (3) = SMB_wbyte(value) not acked
// parameters  : start address = 0...31
//               value  = 0 ... 255
char write_register(char adr, char value)
{
    char status;
    // here comes controller specific code for I2C communication  
    return(status);
 }          

// optimize the response of the sensor by
// setting filters to fast values once an
// interrupt was detected
// determine presence with the over-temperature feature
// call this procedure in case of all CaliPile interrupts 
char presence_detection()
{
    char chipstatus;
    char interruptstatus; 
    char interruptmask;
    char LPsettings; 
    char mask;
    char i;  
    unsigned int dTPamb;
    intstatus new_status;
    char under_temperature_soft; 
      
    //LED = 1;
    
    // read the chip status and 
    // interrupt status to reset the interrupt of the chip
    // if this method is called via an interrupt, make
    // sure the interrupt was not read previously
    // otherwise use the chip status which may
    // be not up-to-date depending on the readout speed   
                      
    read_register(0, 32);
    interruptstatus = SMB_buf[18]; // interrupt status + chip status     
    chipstatus = SMB_buf[19];
     
    // simulate here the interrupt since interrupt pin is not used on this board                 
    // read the interruptmask
    interruptmask = SMB_buf[25];  
    // compare interrupstatus with the mask like the sensor is doing it
    if ((interruptstatus & interruptmask) == 0)
    {
        return 0;
    }      
    
    // initialize the status
    new_status = current_status;
    
    // ************ ambient temperature stability condition check *****************
    // determine the current ambient offset to previously registered one
    TPambient = SMB_buf[10];// & mask;
    TPambient <<= 8;
    TPambient |= SMB_buf[11];
    // divide it by 2 to get rom 16 bit to 15 bit PTAT resolution with a slope of 172 counts/K 
    TPambient >>= 1;
    // initialize the variable the first time
    if (TPambient_was == 0) TPambient_was = TPambient;
    if (TPambient_was > TPambient)
        dTPamb = TPambient_was - TPambient;
    else
        dTPamb = TPambient - TPambient_was;
    if (dTPamb > 30) // did the condition change by more than ~0.2K? Please optimize this condition for your application
    {
        if (current_status == STATpresence)
        {
            // if the situation is not stable, the overtemperature limit is probably not correct
            new_status = STATresetting;
            // indicate with the LED
            for (i = 0; i < 3; i++)
            {
                LED = 0;   
                DEBUGPIN = 0;
                delay_ms(100); 
                LED = 1;     
                DEBUGPIN = 1;
                delay_ms(100);
            }
        }
        TPambient_was = TPambient;
    }    
    // ************ end of temperature stability condition check ******************  

    // use the chip status to capture the current condition
    // interruptstatus = chipstatus;

    // initialize the chip for first time usage after power on
    if (current_status == STATnointerrupt) new_status = STATresetting;

    // entrance condition to presence condition
    // 1. Enter only when the new_status was not redefined by another condition
    // 2. must come from the absence condition
    // 3. must be identified as presence flag after interrupt
    // 4. must have positive sign on presence flag    
    // use chip status not to miss a current condition which was not triggered
    if (new_status == current_status && current_status == STATabsence 
        && (chipstatus & 0x08) != 0 && (chipstatus & 0x80) == 0)   
        //&& (interruptstatus & 0x08) != 0 && (interruptstatus & 0x80) == 0)
    {
        new_status = STATpresence;
    }    
    
    // entrance condition to set fast filter resetting
    // 1. Enter only when the new_status was not redefined by another condition
    // 2. must come from the absence condition
    // 3. must be identified as presence flag after interrupt
    // 4. must have negative sign on presence flag  
    // use chip status not to miss a current condition which was not triggered
    if (new_status == current_status && current_status == STATabsence 
        && (chipstatus & 0x08) != 0 && (chipstatus & 0x80) != 0)
        //&& (interruptstatus & 0x08) != 0 && (interruptstatus & 0x80) != 0)
    {
        new_status = STATresetting;
    }    
    
    // entrance condition to enter the absence status
    // 1. Enter only when the new_status was not redefined by another condition
    // 2. Enter only when the current_status is resetting
    // 3. must have presence flag (filters catched up) 
    // 4. Presence sign flag must indicate negative number 
    // use interrupt status to look into the memory of the sensor
    if (new_status == current_status && current_status == STATresetting
        //&& (chipstatus & 0x08) != 0 && (chipstatus & 0x80) == 0 
        && (interruptstatus & 0x08) != 0 && (interruptstatus & 0x80) == 0
        )
    {
        new_status = STATabsence;
    } 
       
    // ********* check in software the under temperature condition ***********
    if (current_status == STATpresence)
    {
        // please note: using the internal under-temperature feature to generate a trigger 
        // requires the person to approach the device first 
        // to generate a signal which is above the threshold by 64 counts at least
        // otherwise the lamp will turn off again 
        // this is due to the internal hysteresis              
        // numbers can be compared on the µC from time to time in addition which is here reflected     
        under_temperature_soft = 0;
        if (SMB_buf[1] < SMB_buf[28])
        { 
            under_temperature_soft = 1;
        }
        if (SMB_buf[1] == SMB_buf[28] && SMB_buf[2] < SMB_buf[29])  
        {
            under_temperature_soft = 1;
        }     
    }  
    
    // entrance condition to enter the resetting condition
    // 1. Enter only when the new_status was not redefined by another condition
    // 2. Enter only when the current_status is the presence condition
    // 4. Enter only when undertemperature was sensed or better 
    // 4. Enter only when software undertemperature was sensed
    if (new_status == current_status && current_status == STATpresence
        //&& (chipstatus & 0x10) != 0  // use chip status which is the up to date condition not cleared in the interrupt register
        && under_temperature_soft != 0 // use in addition the software undeartemperature in case of weak signals below 64 counts
        )
    {
        new_status = STATresetting;
    } 

    if (new_status != current_status)
    {
        // configuration according to the new status
        if (new_status == STATresetting)
        { 
            // make the background low pass follow the filtered signal nearly with nearly the same speed 
            write_register(20, 0xCD);
            interruptmask = 0x08;
            write_register(25, interruptmask);
            // set now the presence threshold to a value of only 1 so that a sign change
            // will be triggered immediately
            write_register(22, 1);
        } 
        
        if (new_status == STATabsence)
        {
            // restore default filter settings and the threshold
            write_register(20, register_write[20]);
            write_register(22, register_write[22]);
            // prepare the system to go to sleep and listen only to the positive presence interrupt              
            // trigger on the presence feature only and put your host to sleep
            interruptmask = 0x08;                  
            write_register(25, interruptmask);
        }   
        
        if (new_status == STATpresence)
        {
            interruptmask = 0;
            // setup for presence detection via overtemperature
            // store the last L1val to overtemperature for the overtemperature feature in case of absence detection   
            // optionally LP2 can be used but may be less robust an absence misinterpretation due to a warm seat or similar
            // LP1 may give you a shorter distance but is more robust to sence your absence
            TPObjLP1 = 0;
            TPObjLP2 = 0;

            // copy first 16 bit of TPLP1 or TPLP2 to the TPOT

            TPObjLP1 = SMB_buf[5];
            TPObjLP1 <<= 8;
            TPObjLP1 |= SMB_buf[6];
            TPObjLP1 <<= 8;
            TPObjLP1 |= SMB_buf[7];
            TPObjLP1 >>= 4;

            mask = 0x0F;
            TPObjLP2 = (unsigned int)(SMB_buf[7] & mask);
            TPObjLP2 <<= 8;
            TPObjLP2 |= SMB_buf[8];
            TPObjLP2 <<= 8;
            TPObjLP2 |= SMB_buf[9];
            // use LP1 if you want a safer absence measurement
            // use LP2 if you want a better presence measurement
            TPOTthres = TPObjLP1 / 8; // 20 bits maped on 17 bits 
            TPOTthres >>= 1; // last bit is not used   
            // set now the current thereshold
            write_register(29, (char)TPOTthres);
            TPOTthres >>= 8; // shift the first 8 bits to the last position
            write_register(28, (char)TPOTthres);   

            // store the current ambient temperature as a hint if the condition is stable
            TPambient_was = TPambient; 
            
            // set the system to send an interrupt on an undertemperature event 
            // make sure bit 4 in register 26 is set to 0 to notice an undertemperature event!
            interruptmask = 0x10;  
                                      
            // enable the timer to check periodically the temperature stability condition
            // and make sure the timing of readout and real events did not skip one interrupt condition 
            interruptmask |= 0x01;  

            interruptmask |= 0x08;
            write_register(25, interruptmask);
        }

        // indication of new status
        if (new_status == STATpresence)
        {
            LED = 1;   
            DEBUGPIN = 1;
        }
        else
        {
            LED = 0;   
            DEBUGPIN = 0;
        }

        current_status = new_status;
        return 1;
    }  
    return 0;
}

// optimize the response of the sensor by
// setting filters to fast values once an
// interrupt was detected
// call this procedure in case of all interrupts:
// returns 1 if an interrupt was handled
char    host_optimization()
{
    char chipstatus;
    char interruptstatus; 
    char interruptmask;
    char LPsettings; 
    char mask;
    char i;  
    intstatus new_status; 
     
   
    
    //LED = 1;
    
    // read the chip status and 
    // interrupt status to reset the interrupt of the chip
    // if this method is called via an interrupt, make
    // sure the interrupt was not read previously
    // otherwise use the chip status which may
    // be not up-to-date depending on the readout speed   
                      
    read_register(0, 32);
    interruptstatus = SMB_buf[18]; // interrupt status + chip status     
    chipstatus = SMB_buf[19];
     
    // simulate here the interrupt since interrupt pin is not used on this board                 
    // read the interruptmask
    interruptmask = SMB_buf[25];  
    // compare interrupstatus with the mask like the sensor is doing it
    if ((interruptstatus & interruptmask) == 0)
    {
        return 0;
    }

    // initialize the status
    new_status = current_status;

    // use the chip status to capture the current condition
    // interruptstatus = chipstatus;

    // initialize the chip for first time usage after power on
    //if (current_status == STATnointerrupt) new_status = STATresetting;

    // entrance condition to set fast filter setting
    // 1. Enter only when the new_status was not redefined by another condition
    // 2. must come from the no interrupt condition
    // 3. must be identified as presence flag after interrupt
    // 4. must have positive sign on presence flag    
    // use chip status not to miss a current condition which was not triggered
    if (new_status == current_status && current_status == STATnointerrupt 
        && (chipstatus & 0x08) != 0 && (chipstatus & 0x80) == 0)   
        //&& (interruptstatus & 0x08) != 0 && (interruptstatus & 0x80) == 0)
    {
        new_status = STATsetting;
    }    
    
    // entrance condition to set fast filter resetting
    // 1. Enter only when the new_status was not redefined by another condition
    // 2. must come from the no interrupt condition
    // 3. must be identified as presence flag after interrupt
    // 4. must have negative sign on presence flag  
    // use chip status not to miss a current condition which was not triggered
    if (new_status == current_status && current_status == STATnointerrupt 
        && (chipstatus & 0x08) != 0 && (chipstatus & 0x80) != 0)
        //&& (interruptstatus & 0x08) != 0 && (interruptstatus & 0x80) != 0)
    {
        new_status = STATresetting;
    }
    
    // entrance condition to enter the no interrupt status
    // 1. Enter only when the new_status was not redefined by another condition
    // 2. Enter only when the current_status is setting
    // 3. must have presence flag (filters catched up) 
    // 4. Presence sign flag must indicate negative number  
    // use interrupt status to look into the memory of the sensor
    if (new_status == current_status && current_status == STATsetting
        //&& (chipstatus & 0x08) != 0 && (chipstatus & 0x80) != 0  
        && (interruptstatus & 0x08) != 0 && (interruptstatus & 0x80) != 0
        )
    {
        new_status = STATnointerrupt;
    }     
    
    // entrance condition to enter the no interrupt status
    // 1. Enter only when the new_status was not redefined by another condition
    // 2. Enter only when the current_status is resetting
    // 3. must have presence flag (filters catched up) 
    // 4. Presence sign flag must indicate positive number 
    // use interrupt status to look into the memory of the sensor
    if (new_status == current_status && current_status == STATresetting
        //&& (chipstatus & 0x08) != 0 && (chipstatus & 0x80) == 0 
        && (interruptstatus & 0x08) != 0 && (interruptstatus & 0x80) == 0
        )
    {
        new_status = STATnointerrupt;
    }

    if (new_status != current_status)
    {
        // configuration according to the new status
        if (new_status == STATsetting || new_status == STATresetting)
        { 
            // make the background low pass follow the filtered signal nearly with nearly the same speed 
            write_register(20, 0xCD);
            interruptmask = 0x08;
            write_register(25, interruptmask);
            // set now the presence threshold to a value of only 1 so that a sign change
            // will be triggered immediately
            write_register(22, 1);
        } 
        
        if (new_status == STATnointerrupt)
        {
            // restore default filter settings and the threshold
            write_register(20, register_write[20]);
            write_register(22, register_write[22]);
            // prepare the system to go to sleep and listen only to the positive presence interrupt              
            // trigger on the presence feature only and put your host to sleep
            interruptmask = 0x08;                  
            write_register(25, interruptmask);
        }

        // indication of new status
        if (new_status == STATresetting || new_status == STATsetting)
        {
            LED = 1;   
            DEBUGPIN = 1;
        }
        else
        {
            LED = 0;   
            DEBUGPIN = 0;
        }

        current_status = new_status;
        return 1;
    } 
    return 0;
}
          
void main(void)
{
	// ...
	// reload SA from E2PROM
	general_call(0x04);     
	// wait until the device is ready for communication
    delay_us(300);
    // write registers 20 to 29 	
    write_configuration();
	// check the correct settings
    read_configuration();

    while (1){   
        // use either of both: presence_detection or host_optimization
        //while (presence_detection() != 0)
        {
            // repreat checking the sensor as long as the sensor keeps changing its configuration
        } 
        
        while (host_optimization()!=0)
        {
            // repreat checking the sensor as long as the sensor keeps changing its configuration
        }  
		// ...
    }
} // end main

#25

Welcome to the community and good to have a source for first-hand info! :+1:


#26
/* 08/16/2017 Copyright Tlera Corporation
 *
 *  Created by Kris Winer
 *
 This sketch is to operate Excelitas' CaliPile TPiS1S1385/5029 IR Thermopile
 https://media.digikey.com/pdf/Data%20Sheets/Excelitas%20PDFs/TPiS_1S_1385.pdf

 The sketch uses default SDA/SCL pins on 20/21 of the Butterfly development board.
 The CaliPile breakout board has 4K7 pullus on SDA and SCL.

 Library may be used freely and without limit with attribution.

  */
//#include "application.h"
#include <Wire.h>

//https://www.pacer-usa.com/Assets/User/2077-CaliPile_TPiS_1S_1385_5029_27.04.2017.pdf
// CaliPile Registers
#define CALIPILE_TPOBJECT            1
#define CALIPILE_TPAMBIENT           3
#define CALIPILE_TPOBJLP1            5
#define CALIPILE_TPOBJLP2            7
#define CALIPILE_TPAMBLP3           10
#define CALIPILE_TPOBJLP2_FRZN      12
#define CALIPILE_TPPRESENCE         15
#define CALIPILE_TPMOTION           16
#define CALIPILE_TPAMB_SHOCK        17
#define CALIPILE_INTERRUPT_STATUS   18
#define CALIPILE_CHIP_STATUS        19
#define CALIPILE_SLP12              20
#define CALIPILE_SLP3               21
#define CALIPILE_TP_PRES_THLD       22
#define CALIPILE_TP_MOT_THLD        23
#define CALIPILE_TP_AMB_SHOCK_THLD  24
#define CALIPILE_INT_MASK           25
#define CALIPILE_SRC_SELECT         26
#define CALIPILE_TMR_INT            27
#define CALIPILE_TPOT_THR           28

// EEPROM Registers
#define CALIPILE_EEPROM_CONTROL     31
#define CALIPILE_EEPROM_PROTOCOL    32
#define CALIPILE_EEPROM_CHECKSUM    33
#define CALIPILE_EEPROM_LOOKUPNUM   41
#define CALIPILE_EEPROM_PTAT25      42
#define CALIPILE_EEPROM_M           44
#define CALIPILE_EEPROM_U0          46
#define CALIPILE_EEPROM_UOUT1       48
#define CALIPILE_EEPROM_TOBJ1       50
#define CALIPILE_SLAVE_ADDRESS      63

// I2C address when AD0 = AD1 = 0 (default)
#define CALIPILE_ADDRESS 0x0C

// Low-Pass time constants
#define TC_512s    0x00
#define TC_256s    0x01
#define TC_128s    0x02
#define TC_64s     0x03
#define TC_32s     0x04
#define TC_16s     0x05
#define TC_8s      0x08
#define TC_4s      0x09
#define TC_2s      0x0A
#define TC_1s      0x0B
#define TC_0_50s   0x0C
#define TC_0_25s   0x0D

// Sources
#define src_TPOBJ_TPOBJLP2         0x00
#define src_TPOBJLP1_TPOBJLP2      0x01
#define src_TPOBJ_TPOBJLP2_FRZN    0x02
#define src_TPOBJLP1_TPOBJLP2_FRZN 0x03

// Cycle times
#define cycTime_30ms  0x00
#define cycTime_60ms  0x01
#define cycTime_120ms 0x02
#define cycTime_140ms 0x03

// Define pins
#define intPin 4
#define myLed1 D7  // red led
#define myLed2 D7  // green led
#define myLed3 D7  // blue led

bool serialDebug = false, presSign = false, motSign = false;

uint8_t lookUp, rawData[3] = {0, 0, 0}, intStatus, chipStatus, temp;

// Register read variables
uint16_t PTAT25, M, U0, CHECKSUM, TPAMB, TPAMBLP3;
uint32_t TPOBJ, UOUT1, TPOBJLP1, TPOBJLP2, TPOBJLP2FRZN;
uint8_t  TOBJ1, TPPRESENCE, TPMOTION, TPAMBSHK;

// output data for comparisons
float Tamb, Tamblp3, Tobj, Tobjlp1, Tobjlp2, Tobjlp2frzn, Tpres, Tmot, Tambshk, k;

bool newInt = false;

void setup() {

  Serial.begin(115200);
  delay(4000);

  pinMode(intPin, INPUT);

  pinMode(myLed1, OUTPUT);
  digitalWrite(myLed1, LOW);  // Start with leds off, active LOW on Butterfly
  pinMode(myLed2, OUTPUT);
  digitalWrite(myLed2, LOW);
  pinMode(myLed3, OUTPUT);
  digitalWrite(myLed3, LOW);

  Wire.begin(); // set master mode
  Wire.setClock(400000); // I2C frequency at 400 kHz
  delay(1000);

  I2Cscan();

  //Initiate I2C transcations with general call/reload command
  writeByte(0x00, 0x04, 0x00);
  delay(1);

  I2Cscan();


 /* Start of EEPROM operations, just have to do once *************************************************** */
 // Check EEPROM protocol number as a test of I2C communication
  writeByte(CALIPILE_ADDRESS, CALIPILE_EEPROM_CONTROL, 0x80); // enable EEPROM read

  uint8_t c = readByte(CALIPILE_ADDRESS, CALIPILE_EEPROM_PROTOCOL);
  Serial.print("CaliPile EEPROM protocol number is "); Serial.println(c);
  Serial.println("CaliPile EEPROM protocol number should be 3");

  uint8_t d = readByte(CALIPILE_ADDRESS, CALIPILE_SLAVE_ADDRESS);
  Serial.print("CaliPile EEPROM slave address is "); Serial.println(d);
  Serial.println("CaliPile EEPROM slave address should be 140");
  Serial.println(" ");

  // Read the EEPROM calibration constants

  lookUp = readByte(CALIPILE_ADDRESS, CALIPILE_EEPROM_LOOKUPNUM);
  Serial.print("CaliPile LookUpNumber is "); Serial.println(lookUp);

  readBytes(CALIPILE_ADDRESS, CALIPILE_EEPROM_PTAT25, 2, &rawData[0]);
  PTAT25 = ( (uint16_t) rawData[0] << 8) | rawData[1];
  Serial.print("CaliPile PTAT25 is "); Serial.println(PTAT25);

  readBytes(CALIPILE_ADDRESS, CALIPILE_EEPROM_M, 2, &rawData[0]);
  M = ( (uint16_t) rawData[0] << 8) | rawData[1];
  M /= 100;
  Serial.print("CaliPile M is "); Serial.println(M);

  readBytes(CALIPILE_ADDRESS, CALIPILE_EEPROM_U0, 2, &rawData[0]);
  U0 = ( (uint16_t) rawData[0] << 8) | rawData[1];
  U0 += 32768;
  Serial.print("CaliPile U0 is "); Serial.println(U0);

  readBytes(CALIPILE_ADDRESS, CALIPILE_EEPROM_UOUT1, 2, &rawData[0]);
  UOUT1 = ( (uint16_t) rawData[0] << 8) | rawData[1];
  UOUT1 *= 2;
  Serial.print("CaliPile UOUT1 is "); Serial.println(UOUT1);

  TOBJ1 = readByte(CALIPILE_ADDRESS, CALIPILE_EEPROM_TOBJ1);
  Serial.print("CaliPile TOBJ1 is "); Serial.println(TOBJ1);

  readBytes(CALIPILE_ADDRESS, CALIPILE_EEPROM_CHECKSUM, 2, &rawData[0]);
  CHECKSUM = ( (uint16_t) rawData[0] << 8) | rawData[1];
  Serial.print("CaliPile CHECKSUM is supposed to be "); Serial.println(CHECKSUM);

  // Calculate the checksum
  uint16_t sum = 0;
  for(int ii = 35; ii < 64; ii++)
  {
   sum += readByte(CALIPILE_ADDRESS, ii);
  }
  Serial.print("CaliPile CHECKSUM is "); Serial.println(sum + c);

  writeByte(CALIPILE_ADDRESS, CALIPILE_EEPROM_CONTROL, 0x00); // disable EEPROM read
  /* End of EEPROM operations, just have to do once *************************************************** */


  // Initialize the sensor for motion and presence detection
  // Tthr (bit 4), presence (bit(3), motion (bit 2), amb shock (bit 1), timer (bit 0) interrupts allowed
  writeByte(CALIPILE_ADDRESS, CALIPILE_INT_MASK, 0x1C);
  // time constant for LP1 (bits 0 - 3) and LP2 (bits 4 - 7)
  writeByte(CALIPILE_ADDRESS, CALIPILE_SLP12, TC_8s << 4 | TC_1s);
  // select cycle time (bits 0 - 1) for motion detection, source (bits) 2 - 3) for presence detection
  temp = readByte(CALIPILE_ADDRESS, CALIPILE_SRC_SELECT);
  writeByte(CALIPILE_ADDRESS, CALIPILE_SRC_SELECT, temp | src_TPOBJLP1_TPOBJLP2 << 2 | cycTime_30ms);
  // select presence and motion thresholds
  writeByte(CALIPILE_ADDRESS, CALIPILE_TP_PRES_THLD, 0x22); // set at 50 counts
  writeByte(CALIPILE_ADDRESS, CALIPILE_TP_MOT_THLD, 0x0A); // set at 10 counts

  // specify the over temperature interrupt threshold (2 bytes)
  writeByte(CALIPILE_ADDRESS, CALIPILE_TPOT_THR, 0x83); // choose 67,072 counts as threshold
  writeByte(CALIPILE_ADDRESS, (CALIPILE_TPOT_THR + 1), 0x00);
  temp = readByte(CALIPILE_ADDRESS, CALIPILE_SRC_SELECT);
  writeByte(CALIPILE_ADDRESS, CALIPILE_SRC_SELECT, temp | 0x10); // interrupt on exceeding threshold
  // Verify threshole set
  readBytes(CALIPILE_ADDRESS, CALIPILE_TPOT_THR, 2, &rawData[0]);
  uint16_t TPOTTHR = ((uint16_t) rawData[0] << 8) | rawData[1];
  Serial.print("Overtemp threshold = "); Serial.println(TPOTTHR * 2);

  // Construct needed calibration constants (just need to calculate once)
  k = ( (float) (UOUT1 - U0) )/(powf((float)(TOBJ1 + 273.15f), 3.8f) - powf(25.0f + 273.15f, 3.8f) );

  attachInterrupt(intPin, myinthandler, FALLING);  // define interrupt for INT pin output of CaliPile

  // read interrupt status register(s) to unlatch interrupt before entering main loop
  intStatus  = readByte(CALIPILE_ADDRESS, CALIPILE_INTERRUPT_STATUS);
  Serial.print("Int status = "); Serial.println(intStatus, HEX);

  /* end of setup */
}


void loop() {

   if(newInt == true)  // handle interrupt on receipt
   {
    newInt = false;

    // read interrupt status register(s) to clear interrupt
    intStatus  = readByte(CALIPILE_ADDRESS, CALIPILE_INTERRUPT_STATUS);

    if(intStatus & 0x08)
    {
      Serial.println("Presence detected!");
      digitalWrite(myLed2, HIGH); delay(500); digitalWrite(myLed2, LOW); delay(500); digitalWrite(myLed2, HIGH); delay(500); digitalWrite(myLed2, LOW);  // flash D7 led 2 times to indicate Presense Detected.
      if(intStatus & 0x80) presSign = true;
      else presSign = false;
    }

    if(intStatus & 0x04)
    {
      Serial.println("Motion detected!");
      digitalWrite(myLed3, HIGH); delay(250); digitalWrite(myLed3, LOW); delay(250); digitalWrite(myLed3, HIGH); delay(250); digitalWrite(myLed3, LOW); delay(250); digitalWrite(myLed3, HIGH); delay(250); digitalWrite(myLed3, LOW); // flash D7 led 3 times to indicate Motion Detected.
      if(intStatus & 0x40) motSign = true;
      else motSign = false;
      }

    if(intStatus & 0x10)
    {
      Serial.println("Temp threshold exceeded!");
    }

    /* end of interrupt handling */
    }


  // read the ambient temperature
  readBytes(CALIPILE_ADDRESS, CALIPILE_TPAMBIENT, 2, &rawData[0]);
  TPAMB = ( (uint16_t)(rawData[0] & 0x7F) << 8) | rawData[1] ;

  Tamb = 298.15f + ((float)TPAMB - (float)PTAT25) * (1.0f/(float) M);

  // read the object temperature
  readBytes(CALIPILE_ADDRESS, CALIPILE_TPOBJECT, 3, &rawData[0]);
  TPOBJ = ( (uint32_t) ( (uint32_t)rawData[0] << 24) | ( (uint32_t)rawData[1] << 16) | (rawData[2] & 0x80) << 8) >> 15;

  float temp0 = powf(Tamb, 3.8f);
  float temp1 = ( ((float) TPOBJ) - ((float) U0)  ) / k ;
  Tobj = powf( (temp0 + temp1), 0.2631578947f );

  // Read the time-integrated registers

  // 20-bit wide, divide by 8 to compare with TPOBJ
  readBytes(CALIPILE_ADDRESS, CALIPILE_TPOBJLP1, 3, &rawData[0]);
  TPOBJLP1 = ( ((uint32_t) rawData[0] << 16) | ((uint32_t) rawData[1] << 8) | (rawData[2] & 0xF0) ) >> 4;
  TPOBJLP1 /= 8;

  // 20-bit wide, divide by 8 to compare with TPOBJ
  readBytes(CALIPILE_ADDRESS, CALIPILE_TPOBJLP2, 3, &rawData[0]);
  TPOBJLP2 = ((uint32_t) (rawData[0] & 0x0F) << 16) | ((uint32_t) rawData[1] << 8) | rawData[2] ;
  TPOBJLP2 /= 8;

  // 16-bit wide, divide by 2 to compare with TPAMB
  readBytes(CALIPILE_ADDRESS, CALIPILE_TPAMBLP3, 2, &rawData[0]);
  TPAMBLP3 = ((uint16_t) rawData[0] << 8) | rawData[1];
  TPAMBLP3 /= 2;

  // 24-bit wide, divide by 128 to compare with TPOBJ
  readBytes(CALIPILE_ADDRESS, CALIPILE_TPOBJLP2_FRZN, 3, &rawData[0]);
  TPOBJLP2FRZN = ((uint32_t) rawData[0] << 16) | ((uint32_t) rawData[1] << 8) | rawData[2];
  TPOBJLP2FRZN /= 128;

  TPPRESENCE = readByte(CALIPILE_ADDRESS, CALIPILE_TPPRESENCE);
  TPMOTION   = readByte(CALIPILE_ADDRESS, CALIPILE_TPMOTION);
  TPAMBSHK   = readByte(CALIPILE_ADDRESS, CALIPILE_TPAMB_SHOCK);

  if(serialDebug)
  {
    Serial.print("Tambient = "); Serial.print(Tamb, 2); Serial.println(" K");
    Serial.print("TPAMP = "); Serial.println(TPAMB);
    Serial.print("TAMBLP3 = "); Serial.println(TPAMBLP3);
    Serial.println(" ");

    Serial.print("Tobj = "); Serial.print(Tobj, 2); Serial.println(" K");
    Serial.print("TPOBJ = "); Serial.println(TPOBJ);
    Serial.print("TPOBJLP1 = "); Serial.println(TPOBJLP1);
    Serial.print("TPOBJLP2 = "); Serial.println(TPOBJLP2);
    Serial.print("TPOBJLP2FRZN = "); Serial.println(TPOBJLP2FRZN);
    Serial.println(" ");

     if(presSign)
    {
      Serial.print("TPPRESENCE = ");   Serial.println(-1 * TPPRESENCE);
    }
    else
    {
       Serial.print("TPPRESENCE = ");   Serial.println(TPPRESENCE);
    }

    if(motSign)
    {
      Serial.print("TPMOTION = ");   Serial.println(-1 * TPMOTION);
    }
    else
    {
       Serial.print("TPMOTION = ");   Serial.println(TPMOTION);
    }

    Serial.print("TAMBSHK = ");    Serial.println(TPAMBSHK);
    Serial.println(" ");
  }

// Output for serial plotter
//  Serial.print((Tamb - 273.15));  Serial.print("  "); Serial.print((Tobj - 273.15)); Serial.println("  ");

//   Serial.print((TPAMB)); Serial.print("  "); Serial.print((TPAMBLP3)); Serial.println("  ");

//   Serial.print((TPOBJ)); Serial.print("  "); Serial.print((TPOBJLP1)); Serial.print("  ");
//   Serial.print((TPOBJLP2)); Serial.print("  "); Serial.print((TPOBJLP2FRZN)); Serial.println("  ");

//  digitalWrite(myLed1, LOW); delay(10); digitalWrite(myLed1, HIGH);  delay(500);

delay(250);
  /* end of main loop */
}


/* Useful functions */
void myinthandler()
{
  newInt = true;
}


// I2C scan function
void I2Cscan()
{
// scan for i2c devices
  byte error, address;
  int nDevices;

  Serial.println("Scanning...");

  nDevices = 0;
  for(address = 1; address < 127; address++ )
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmission to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address<16)
        Serial.print("0");
      Serial.print(address,HEX);
      Serial.println("  !");

      nDevices++;
    }
    else if (error==4)
    {
      Serial.print("Unknown error at address 0x");
      if (address<16)
        Serial.print("0");
      Serial.println(address,HEX);
    }
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");

}


// I2C read/write functions for the CaliPile sensor

  void writeByte(uint8_t address, uint8_t subAddress, uint8_t data)
{
  Wire.beginTransmission(address);  // Initialize the Tx buffer
  Wire.write(subAddress);           // Put slave register address in Tx buffer
  Wire.write(data);                 // Put data in Tx buffer
  Wire.endTransmission();           // Send the Tx buffer
}

  uint8_t readByte(uint8_t address, uint8_t subAddress)
{
  uint8_t data; // `data` will store the register data
  Wire.beginTransmission(address);         // Initialize the Tx buffer
  Wire.write(subAddress);                  // Put slave register address in Tx buffer
  Wire.endTransmission(false);             // Send the Tx buffer, but send a restart to keep connection alive
  Wire.requestFrom(address, 1);            // Read one byte from slave register address
  data = Wire.read();                      // Fill Rx buffer with result
  return data;                             // Return data read from slave register
}

  void readBytes(uint8_t address, uint8_t subAddress, uint8_t count, uint8_t * dest)
{
  Wire.beginTransmission(address);   // Initialize the Tx buffer
  Wire.write(subAddress);            // Put slave register address in Tx buffer
  Wire.endTransmission(false);       // Send the Tx buffer, but send a restart to keep connection alive
  uint8_t i = 0;
  Wire.requestFrom(address, count);  // Read bytes from slave register address
  while (Wire.available()) {dest[i++] = Wire.read(); } // Put read results in the Rx buffer
}

I use this code in my Arduino nano board with TPIS1S1385 Motion detection is working good but presence detection not work properly. human object in front of device serial port not print “Presence detected!” continuously. how can i set value to presence detection in my code…???


#27

I never got this sensor to work how I wanted.

These ended up being better.