Read temperature using a Timer

Hi @LukeUSMC

I followed your example however both my code and your example code fail to compile; with the error:

timetest1.cpp: In function ‘void loop()’:
timetest1.cpp:60:20: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
Particle.variable(“tempFahrenheit”, &fahrenheit, DOUBLE);
^
timetest1.cpp: In function ‘void publishData()’:
timetest1.cpp:87:37: warning: precision used with ‘%c’ gnu_printf format [-Wformat=]
Alarm alarm;
^
timetest1.cpp:87:37: warning: format ‘%c’ expects argument of type ‘int’, but argument 3 has type ‘double’ [-Wformat=]
timetest1.cpp:87:37: warning: precision used with ‘%c’ gnu_printf format [-Wformat=]
timetest1.cpp:87:37: warning: format ‘%c’ expects argument of type ‘int’, but argument 3 has type ‘double’ [-Wformat=]
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::pinModeFastOutput()': Particle-OneWire/Particle-OneWire.cpp:128: multiple definition ofOneWire::OneWire(unsigned short)’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:4: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::pinModeFastOutput()': Particle-OneWire/Particle-OneWire.cpp:128: multiple definition ofOneWire::OneWire(unsigned short)’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:4: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::pinModeFastOutput()': Particle-OneWire/Particle-OneWire.cpp:140: multiple definition ofOneWire::reset()’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:16: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::write_bit(unsigned char)': Particle-OneWire/Particle-OneWire.cpp:177: multiple definition ofOneWire::write_bit(unsigned char)’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:53: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::read_bit()': Particle-OneWire/Particle-OneWire.cpp:210: multiple definition ofOneWire::read_bit()’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:86: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::write(unsigned char, unsigned char)': Particle-OneWire/Particle-OneWire.cpp:239: multiple definition ofOneWire::write(unsigned char, unsigned char)’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:115: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::write_bytes(unsigned char const*, unsigned short, bool)': Particle-OneWire/Particle-OneWire.cpp:256: multiple definition ofOneWire::write_bytes(unsigned char const*, unsigned short, bool)’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:132: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::read()': Particle-OneWire/Particle-OneWire.cpp:273: multiple definition ofOneWire::read()’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:149: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::read_bytes(unsigned char*, unsigned short)': Particle-OneWire/Particle-OneWire.cpp:284: multiple definition ofOneWire::read_bytes(unsigned char*, unsigned short)’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:160: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::select(unsigned char const*)': Particle-OneWire/Particle-OneWire.cpp:292: multiple definition ofOneWire::select(unsigned char const*)’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:168: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::skip()': Particle-OneWire/Particle-OneWire.cpp:304: multiple definition ofOneWire::skip()’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:180: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::depower()': Particle-OneWire/Particle-OneWire.cpp:307: multiple definition ofOneWire::depower()’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:183: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::reset_search()': Particle-OneWire/Particle-OneWire.cpp:323: multiple definition ofOneWire::reset_search()’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:199: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::target_search(unsigned char)': Particle-OneWire/Particle-OneWire.cpp:339: multiple definition ofOneWire::target_search(unsigned char)’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:215: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::search(unsigned char*)': Particle-OneWire/Particle-OneWire.cpp:365: multiple definition ofOneWire::search(unsigned char*)’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:241: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::crc8(unsigned char*, unsigned char)': Particle-OneWire/Particle-OneWire.cpp:497: multiple definition ofOneWire::crc8(unsigned char*, unsigned char)’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:373: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::crc16(unsigned char const*, unsigned short, unsigned short)': Particle-OneWire/Particle-OneWire.cpp:518: multiple definition ofOneWire::crc16(unsigned char const*, unsigned short, unsigned short)’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:394: first defined here
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function OneWire::check_crc16(unsigned char const*, unsigned short, unsigned char const*, unsigned short)': Particle-OneWire/Particle-OneWire.cpp:512: multiple definition ofOneWire::check_crc16(unsigned char const*, unsigned short, unsigned char const*, unsigned short)’
…/…/…/build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:388: first defined here
collect2: error: ld returned 1 exit status
make: *** [32b71066dfb399eab69ef17a60fe7786434beddc7a148bf503d5d0910dfb.elf] Error 1
Error: Could not compile. Please review your code.

Here is my updated code below…

Many thanks

Andy

// This #include statement was automatically added by the Particle IDE.
#include "Particle-OneWire/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"

// Globals
DS18B20 ds18b20 = DS18B20(D2); //Sets Pin D2 for Water Temp Sensor

int LED = D7; // pin for LED
int PIR = D3; //pin for PIR motion sensor

char szInfo[64];
float pubTemp;
double celsius;
double fahrenheit;
unsigned int Metric_Publish_Rate = 30000;
unsigned int MetricnextPublishTime;
int DS18B20nextSampleTime;
int DS18B20_SAMPLE_INTERVAL = 2500;
int dsAttempts = 0;

Timer ledTimer(1000, blink_led);

class Alarm {
  public:
    Alarm() {
        Particle.function("alarm", &Alarm::alarm, this);
    }
    
    int status;

    int alarm(String command) {
        if(command == "reset"){
            status = 0;
        }
        if(command == "panic"){
            status = 1;
        }
        else {
            status = -1;
        }
        return status;     
    }
};

void setup()
{
    Time.zone(-5);
    Particle.syncTime();
    Particle.variable("tempCelsius", &celsius, DOUBLE);
    Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
    pinMode(LED, OUTPUT); // Use for a simple test of the led on or off by subscribing to a topical called led
    pinMode(PIR, INPUT_PULLUP); // Initialize pin as input with an internal pull-up resistor
    ledTimer.start();
    attachInterrupt(PIR, motion_detected, RISING);
    Serial.begin(9600);
}

void loop() 
{
    if (millis() > DS18B20nextSampleTime){
        getTemp();
    }

    if (millis() > MetricnextPublishTime){
        Serial.println("Publishing now.");
        publishData();
    }
}

void blink_led()
{
    digitalWrite(LED, !digitalRead(LED));
}

void motion_detected()
{
    Alarm alarm;
    alarm.status = 1;
    Particle.publish("photon/motion/detected", "1");
}

void publishData() 
{
    if(!ds18b20.crcCheck()){
        return;
    }
    sprintf(szInfo, "%2.2c", celsius);
    Particle.publish("photon/temperature/celsius", szInfo, PRIVATE);
    sprintf(szInfo, "%2.2f", fahrenheit);
    Particle.publish("photon/temperature/fahrenheit", szInfo, PRIVATE);
    MetricnextPublishTime = millis() + Metric_Publish_Rate;
}

void getTemp()
{
    if(!ds18b20.search()){
        ds18b20.resetsearch();
        celsius = ds18b20.getTemperature();
        Serial.println(celsius);
        while (!ds18b20.crcCheck() && dsAttempts < 4){
            Serial.println("Caught bad value.");
            dsAttempts++;
            Serial.print("Attempts to Read: ");
            Serial.println(dsAttempts);
            if (dsAttempts == 3){
                delay(1000);
            }
            ds18b20.resetsearch();
            celsius = ds18b20.getTemperature();
            continue;
        }
        dsAttempts = 0;
        fahrenheit = ds18b20.convertToFahrenheit(celsius);
        DS18B20nextSampleTime = millis() + DS18B20_SAMPLE_INTERVAL;
        Serial.println(fahrenheit);
    }
}

If you are using the DS18B20 library, you need to remove Particle-OneWire since both define the same functions, hence the hard to misunderstand

To use the library correctly, you should have these includes

#include "DS18B20/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"

@LukeUSMC, for the Particle Build (Web IDE), you might like to correct these includes and also fix the build error about missing displayupdate(); (ds18b20_test.ino:30:17: error: 'displayupdate' was not declared in this scope).

Hi @ScruffR

I made the amends as advised but still get the same errors:

timetest1.cpp: In function 'void loop()':
timetest1.cpp:61:20: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
     Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
                    ^
timetest1.cpp: In function 'void publishData()':
timetest1.cpp:88:37: warning: precision used with '%c' gnu_printf format [-Wformat=]
     Alarm alarm;
                                     ^
timetest1.cpp:88:37: warning: format '%c' expects argument of type 'int', but argument 3 has type 'double' [-Wformat=]
timetest1.cpp:88:37: warning: precision used with '%c' gnu_printf format [-Wformat=]
timetest1.cpp:88:37: warning: format '%c' expects argument of type 'int', but argument 3 has type 'double' [-Wformat=]
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::pinModeFastOutput()':
Particle-OneWire/Particle-OneWire.cpp:128: multiple definition of `OneWire::OneWire(unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:4: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::pinModeFastOutput()':
Particle-OneWire/Particle-OneWire.cpp:128: multiple definition of `OneWire::OneWire(unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:4: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::pinModeFastOutput()':
Particle-OneWire/Particle-OneWire.cpp:140: multiple definition of `OneWire::reset()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:16: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::write_bit(unsigned char)':
Particle-OneWire/Particle-OneWire.cpp:177: multiple definition of `OneWire::write_bit(unsigned char)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:53: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::read_bit()':
Particle-OneWire/Particle-OneWire.cpp:210: multiple definition of `OneWire::read_bit()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:86: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::write(unsigned char, unsigned char)':
Particle-OneWire/Particle-OneWire.cpp:239: multiple definition of `OneWire::write(unsigned char, unsigned char)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:115: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::write_bytes(unsigned char const*, unsigned short, bool)':
Particle-OneWire/Particle-OneWire.cpp:256: multiple definition of `OneWire::write_bytes(unsigned char const*, unsigned short, bool)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:132: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::read()':
Particle-OneWire/Particle-OneWire.cpp:273: multiple definition of `OneWire::read()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:149: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::read_bytes(unsigned char*, unsigned short)':
Particle-OneWire/Particle-OneWire.cpp:284: multiple definition of `OneWire::read_bytes(unsigned char*, unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:160: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::select(unsigned char const*)':
Particle-OneWire/Particle-OneWire.cpp:292: multiple definition of `OneWire::select(unsigned char const*)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:168: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::skip()':
Particle-OneWire/Particle-OneWire.cpp:304: multiple definition of `OneWire::skip()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:180: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::depower()':
Particle-OneWire/Particle-OneWire.cpp:307: multiple definition of `OneWire::depower()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:183: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::reset_search()':
Particle-OneWire/Particle-OneWire.cpp:323: multiple definition of `OneWire::reset_search()'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:199: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::target_search(unsigned char)':
Particle-OneWire/Particle-OneWire.cpp:339: multiple definition of `OneWire::target_search(unsigned char)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:215: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::search(unsigned char*)':
Particle-OneWire/Particle-OneWire.cpp:365: multiple definition of `OneWire::search(unsigned char*)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:241: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::crc8(unsigned char*, unsigned char)':
Particle-OneWire/Particle-OneWire.cpp:497: multiple definition of `OneWire::crc8(unsigned char*, unsigned char)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:373: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::crc16(unsigned char const*, unsigned short, unsigned short)':
Particle-OneWire/Particle-OneWire.cpp:518: multiple definition of `OneWire::crc16(unsigned char const*, unsigned short, unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:394: first defined here
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o): In function `OneWire::check_crc16(unsigned char const*, unsigned short, unsigned char const*, unsigned short)':
Particle-OneWire/Particle-OneWire.cpp:512: multiple definition of `OneWire::check_crc16(unsigned char const*, unsigned short, unsigned char const*, unsigned short)'
../../../build/target/user/platform-6/libuser.a(Particle-OneWire.o):DS18B20/Particle-OneWire.cpp:388: first defined here
collect2: error: ld returned 1 exit status
make: *** [d2c72077268d5875b0e39b41a3c3a9caa52b0d9c85d89ffdf319f7968b3c.elf] Error 1

This is my code:

// This #include statement was automatically added by the Particle IDE.
//#include "Particle-OneWire/Particle-OneWire.h"
#include "DS18B20/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"

// Globals
DS18B20 ds18b20 = DS18B20(D2); //Sets Pin D2 for Water Temp Sensor

int LED = D7; // pin for LED
int PIR = D3; //pin for PIR motion sensor

char szInfo[64];
float pubTemp;
double celsius;
double fahrenheit;
unsigned int Metric_Publish_Rate = 30000;
unsigned int MetricnextPublishTime;
int DS18B20nextSampleTime;
int DS18B20_SAMPLE_INTERVAL = 2500;
int dsAttempts = 0;

Timer ledTimer(1000, blink_led);

class Alarm {
  public:
    Alarm() {
        Particle.function("alarm", &Alarm::alarm, this);
    }
    
    int status;

    int alarm(String command) {
        if(command == "reset"){
            status = 0;
        }
        if(command == "panic"){
            status = 1;
        }
        else {
            status = -1;
        }
        return status;     
    }
};

void setup()
{
    Time.zone(-5);
    Particle.syncTime();
    Particle.variable("tempCelsius", &celsius, DOUBLE);
    Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
    pinMode(LED, OUTPUT); // Use for a simple test of the led on or off by subscribing to a topical called led
    pinMode(PIR, INPUT_PULLUP); // Initialize pin as input with an internal pull-up resistor
    ledTimer.start();
    attachInterrupt(PIR, motion_detected, RISING);
    Serial.begin(9600);
}

void loop() 
{
    if (millis() > DS18B20nextSampleTime){
        getTemp();
    }

    if (millis() > MetricnextPublishTime){
        Serial.println("Publishing now.");
        publishData();
    }
}

void blink_led()
{
    digitalWrite(LED, !digitalRead(LED));
}

void motion_detected()
{
    Alarm alarm;
    alarm.status = 1;
    Particle.publish("photon/motion/detected", "1");
}

void publishData() 
{
    if(!ds18b20.crcCheck()){
        return;
    }
    sprintf(szInfo, "%2.2c", celsius);
    Particle.publish("photon/temperature/celsius", szInfo, PRIVATE);
    sprintf(szInfo, "%2.2f", fahrenheit);
    Particle.publish("photon/temperature/fahrenheit", szInfo, PRIVATE);
    MetricnextPublishTime = millis() + Metric_Publish_Rate;
}

void getTemp()
{
    if(!ds18b20.search()){
        ds18b20.resetsearch();
        celsius = ds18b20.getTemperature();
        Serial.println(celsius);
        while (!ds18b20.crcCheck() && dsAttempts < 4){
            Serial.println("Caught bad value.");
            dsAttempts++;
            Serial.print("Attempts to Read: ");
            Serial.println(dsAttempts);
            if (dsAttempts == 3){
                delay(1000);
            }
            ds18b20.resetsearch();
            celsius = ds18b20.getTemperature();
            continue;
        }
        dsAttempts = 0;
        fahrenheit = ds18b20.convertToFahrenheit(celsius);
        DS18B20nextSampleTime = millis() + DS18B20_SAMPLE_INTERVAL;
        Serial.println(fahrenheit);
    }
}

Many thanks

Andy

Have you actually removed the Particle-OneWire library from your project by clicking the black circle (X) next to the library name?

Hi @ScruffR

My bad still getting used to the web IDE, its all compiling now thanks.

Andy

1 Like

Hi,

So I have my code deployed, the led flashes constantly as expected. I also get temperatures in both C and F logged out to my dashboard. However as soon as the PIR motion sensor is triggered, the code clearly stops and the led stays lit solid.

There is obviously an issue with the interrupt but I’m not sure why?

Many thanks

Andy

// This #include statement was automatically added by the Particle IDE.
//#include "Particle-OneWire/Particle-OneWire.h"
#include "DS18B20/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"

// Globals
DS18B20 ds18b20 = DS18B20(D2); //Sets Pin D2 for Water Temp Sensor

int LED = D7; // pin for LED
int PIR = D3; //pin for PIR motion sensor

float pubTemp;
double celsius;
double fahrenheit;
unsigned int Metric_Publish_Rate = 30000;
unsigned int MetricnextPublishTime;
int DS18B20nextSampleTime;
int DS18B20_SAMPLE_INTERVAL = 2500;
int dsAttempts = 0;

Timer ledTimer(1000, blink_led);

class Alarm {
  public:
    Alarm() {
        Particle.function("alarm", &Alarm::alarm, this);
    }
    
    int status;

    int alarm(String command) {
        if(command == "reset"){
            status = 0;
        }
        if(command == "panic"){
            status = 1;
        }
        else {
            status = -1;
        }
        return status;     
    }
};

void setup()
{
    Time.zone(-5);
    Particle.syncTime();
    Particle.variable("tempCelsius", &celsius, DOUBLE);
    Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
    pinMode(LED, OUTPUT); // Use for a simple test of the led on or off by subscribing to a topical called led
    pinMode(PIR, INPUT_PULLUP); // Initialize pin as input with an internal pull-up resistor
    ledTimer.start();
    attachInterrupt(PIR, motion_detected, RISING);
    Serial.begin(9600);
}

void loop() 
{
    if (millis() > DS18B20nextSampleTime){
        getTemp();
    }

    if (millis() > MetricnextPublishTime){
        Serial.println("Publishing now.");
        publishData();
    }
}

void blink_led()
{
    digitalWrite(LED, !digitalRead(LED));
}

void motion_detected()
{
    Alarm alarm;
    alarm.status = 1;
    Particle.publish("photon/motion/detected", "1");
}

void publishData() 
{
    if(!ds18b20.crcCheck()){
        return;
    }
    Particle.publish("photon/temperature/celsius", String(celsius));
    Particle.publish("photon/temperature/fahrenheit", String(fahrenheit));
    MetricnextPublishTime = millis() + Metric_Publish_Rate;
}

void getTemp()
{
    if(!ds18b20.search()){
        ds18b20.resetsearch();
        celsius = ds18b20.getTemperature();
        Serial.println(celsius);
        while (!ds18b20.crcCheck() && dsAttempts < 4){
            Serial.println("Caught bad value.");
            dsAttempts++;
            Serial.print("Attempts to Read: ");
            Serial.println(dsAttempts);
            if (dsAttempts == 3){
                delay(1000);
            }
            ds18b20.resetsearch();
            celsius = ds18b20.getTemperature();
            continue;
        }
        dsAttempts = 0;
        fahrenheit = ds18b20.convertToFahrenheit(celsius);
        DS18B20nextSampleTime = millis() + DS18B20_SAMPLE_INTERVAL;
        Serial.println(fahrenheit);
    }
}

Don’t publish inside an ISR and I wouldn’t create an automatic object inside and ISR either.

Rather set a flag in your ISR and act upon it in your loop().

Shoot. I thought I had already corrected that. I’ll get it fixed. Thanks.

1 Like

The example app has been updated and corrected. Version 0.1.02 is published and verified without warning. Sorry about that…supposed to be helping not confusing folks!

DS18B20 0.1.02
1 Like

Hi

Thanks for the help, I’ve tried a similar idea to the getTemp() check in the main loop, however I still observe the same behaviour in terms of the code freezing. I guess I’m still missing something?

Also out of interest are there any other techniques that I could employ or is the typical method to set flags and pick them up in the main loop?

Many thanks

Andy

// This #include statement was automatically added by the Particle IDE.
#include "DS18B20/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"

// Globals
DS18B20 ds18b20 = DS18B20(D2); //Sets Pin D2 for Water Temp Sensor

int LED = D7; // pin for LED
int PIR = D3; //pin for PIR motion sensor

float pubTemp;
double celsius;
double fahrenheit;
unsigned int Metric_Publish_Rate = 30000;
unsigned int MetricnextPublishTime;
int DS18B20nextSampleTime;
int DS18B20_SAMPLE_INTERVAL = 2500;
int dsAttempts = 0;

Timer ledTimer(1000, blink_led);

class Alarm {
  public:
    Alarm() {
        Particle.function("alarm", &Alarm::alarm, this);
    }
    
    int status;

    int alarm(String command) {
        if(command == "reset"){
            status = 0;
        }
        if(command == "panic"){
            status = 1;
        }
        else {
            status = -1;
        }
        return status;     
    }
};

void setup()
{
    Time.zone(-5);
    Particle.syncTime();
    Particle.variable("tempCelsius", &celsius, DOUBLE);
    Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
    pinMode(LED, OUTPUT); // Use for a simple test of the led on or off by subscribing to a topical called led
    pinMode(PIR, INPUT_PULLUP); // Initialize pin as input with an internal pull-up resistor
    ledTimer.start();
    attachInterrupt(PIR, motion_detected, RISING);
    Serial.begin(9600);
}

void loop() 
{
    if (millis() > DS18B20nextSampleTime){
        getTemp();
    }

    if (millis() > MetricnextPublishTime){
        Serial.println("Publishing now.");
        publishData();
    }
    
    Alarm alarm;
    if (alarm.status == 1)
    {
        raiseAlarm();
    }
}

void blink_led()
{
    digitalWrite(LED, !digitalRead(LED));
}

void motion_detected()
{
    Alarm alarm;
    alarm.status = 1;
}

void resetAlarm()
{
    Alarm alarm;
    alarm.status = 0;
}

void raiseAlarm()
{
    Particle.publish("photon/motion/detected", "1");
    resetAlarm();
}

void publishData() 
{
    if(!ds18b20.crcCheck()){
        return;
    }
    Particle.publish("photon/temperature/celsius", String(celsius));
    Particle.publish("photon/temperature/fahrenheit", String(fahrenheit));
    MetricnextPublishTime = millis() + Metric_Publish_Rate;
}

void getTemp()
{
    if(!ds18b20.search()){
        ds18b20.resetsearch();
        celsius = ds18b20.getTemperature();
        Serial.println(celsius);
        while (!ds18b20.crcCheck() && dsAttempts < 4){
            Serial.println("Caught bad value.");
            dsAttempts++;
            Serial.print("Attempts to Read: ");
            Serial.println(dsAttempts);
            if (dsAttempts == 3){
                delay(1000);
            }
            ds18b20.resetsearch();
            celsius = ds18b20.getTemperature();
            continue;
        }
        dsAttempts = 0;
        fahrenheit = ds18b20.convertToFahrenheit(celsius);
        DS18B20nextSampleTime = millis() + DS18B20_SAMPLE_INTERVAL;
        Serial.println(fahrenheit);
    }
}

you create an instance of Alarm class
you then assign “1” to Alarm::status
then it all gets destroyed when the ISR goes out of scope…

It isn’t really clear (to me) what you are trying to do with Alarm class…

have you considered a slightly simpler approach like this (uncompiled, untested):

// This #include statement was automatically added by the Particle IDE.
#include "DS18B20/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"

// Globals
DS18B20 ds18b20 = DS18B20(D2); //Sets Pin D2 for Water Temp Sensor

int LED = D7; // pin for LED
int PIR = D3; //pin for PIR motion sensor

float pubTemp;
double celsius;
double fahrenheit;
const int Metric_Publish_Rate = 30000;
const int DS18B20_SAMPLE_INTERVAL = 2500;
unsigned long metricPublishTime = 0;
unsigned long tempSampleTime = 0;

volatile bool isrFlag = false;

int dsAttempts = 0;

Timer ledTimer(1000, blink_led);

// class Alarm {
//   public:
//     Alarm() {
//         Particle.function("alarm", &Alarm::alarm, this);
//     }
//     
//     int status;
// 
//     int alarm(String command) {
//         if(command == "reset"){
//             status = 0;
//         }
//         if(command == "panic"){
//             status = 1;
//         }
//         else {
//             status = -1;
//         }
//         return status;     
//     }
// };

void setup()
{
    Time.zone(-5);
    Particle.syncTime();
    Particle.variable("tempCelsius", &celsius, DOUBLE);
    Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
    pinMode(LED, OUTPUT); // Use for a simple test of the led on or off by subscribing to a topical called led
    pinMode(PIR, INPUT_PULLUP); // Initialize pin as input with an internal pull-up resistor
    ledTimer.start();
    attachInterrupt(PIR, motion_detected, RISING);
    Serial.begin(9600);
}

void loop() 
{
    if (millis() - tempSampleTime > DS18B20_SAMPLE_INTERVAL)
    {
        getTemp();
        tempSampleTime = millis();
    }

    if (millis() -  metricPublishTime > Metric_Publish_Rate)
    {
        Serial.println("Publishing now.");
        publishData();
        metricPublishTime = millis();
    }
    
    if (isrFlag)
    {
      Particle.publish("photon/motion/detected", "1");
      isrFlag = false;
    }
}

void blink_led()
{
    digitalWrite(LED, !digitalRead(LED));
}

void motion_detected()
{
  isrFlag = true;
}

//void resetAlarm()
//{
//    Alarm alarm;
//    alarm.status = 0;
//}

//void raiseAlarm()
//{
//    Particle.publish("photon/motion/detected", "1");
//    resetAlarm();
//}

void publishData() 
{
    if(!ds18b20.crcCheck()){
        return;
    }
    Particle.publish("photon/temperature/celsius", String(celsius));
    Particle.publish("photon/temperature/fahrenheit", String(fahrenheit));
}

void getTemp()
{
    if(!ds18b20.search()){
        ds18b20.resetsearch();
        celsius = ds18b20.getTemperature();
        Serial.println(celsius);
        while (!ds18b20.crcCheck() && dsAttempts < 4){
            Serial.println("Caught bad value.");
            dsAttempts++;
            Serial.print("Attempts to Read: ");
            Serial.println(dsAttempts);
            if (dsAttempts == 3){
                delay(1000);
            }
            ds18b20.resetsearch();
            celsius = ds18b20.getTemperature();
            continue;
        }
        dsAttempts = 0;
        fahrenheit = ds18b20.convertToFahrenheit(celsius);
        Serial.println(fahrenheit);
    }
}

I agree with @BulldogLowell, there is a lot of object construction/destruction going on in your code.

Whenever you write

  Alarm alarm;

You create a new object which is unaware of any state any previous instance of alarm had and it will be destroyed once your code leaves the function where you created it.

This was also the point of this sentence

I think you need to readup about scope of automatic variables and objects.
Meanwhile rather use a global instance of Alarm (or use static class fields).

1 Like

I think perhaps what I was trying to get at was that I wanted to do it in a way I could have a timer and not block the main loop with a delay.

Perhaps to elaborate this idea further if we consider that a motion trigger only triggers an alert to be fired every 5 seconds. So you cannot retrigger within five seconds. I have two thoughts on this, one I can probably work out which is to trigger a timer which I check with millis() and the other which I think I’ve sniffing around the idea of; is if there is a more advanced technique to have multiple threads running perhaps leveraging more within the FreeRTOS library (of which I have no experience). I’ve seen the cosa arduino library which is interesting, but wondered if something similar is possible with the photon?

Many thanks

Andy

I’d suggest before diving into any multithreading aproach is to get an in-depth understanding and good coding practice for sigle-threading.

Multithreading is a very powerful tool, but can hold traps and snares for you if you don’t exactly know what you are doing (shared resources, race conditions, synchronization, …).

Hi @ScruffR

I’m more used to C# with thread pools, background workers and delegates, although I mainly do web these days so haven’t had the need for sometime, but I suppose this is a totally different kettle of fish.

So I’ve included my code below this is now working as expected, I can log motion interrupts from the PIR on a minimum defined interval. I have my led blinking away and temperature readings are coming through on thee timed interval I specified. Tbh I haven’t tried the particle variables and functions, but I’m sure they are fine.

This is all entirely hypothetical at the moment, what I have wired to get has no real world function for me, but its really great to get your feedback on how I can do these types of tasks better or differently, which may be to keep things simple or try more complex things. I’ve been told a thousand times I over complicate things, but I really do like to try more complex things to help understand the other options available and how I can better do things. So if you don’t mind and you have the time can you give me some pointers on how to make my code better (can be simpler). Also on the flip side, are there any more advanced techniques I can employ?

// This #include statement was automatically added by the Particle IDE.
#include "DS18B20/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"

// Globals
DS18B20 ds18b20 = DS18B20(D2); //Sets Pin D2 for Water Temp Sensor

int LED = D7; // pin for LED
int PIR = D3; //pin for PIR motion sensor

float pubTemp;
double celsius;
double fahrenheit;
unsigned int Metric_Publish_Rate = 30000;
unsigned int MetricnextPublishTime;
int DS18B20nextSampleTime;
int DS18B20_SAMPLE_INTERVAL = 2500;
int dsAttempts = 0;
int PIRnextSampleTime;
int PIR_SAMPLE_INTERVAL = 3000;
int alarmStatus;

Timer ledTimer(1000, blink_led);

int alarm(String command);

int alarm(String command)
{
    if(command == "reset")
    {
        alarmStatus = 0;
        return 0;
    }
    if(command == "panic")
    {
        alarmStatus = 1;
        return 1;
    }
    else 
    {
        alarmStatus = -1;
        return -1;
    }
}

void setup()
{
    Time.zone(-5);
    Particle.syncTime();
    Particle.function("alarm", alarm);
    Particle.variable("tempCelsius", &celsius, DOUBLE);
    Particle.variable("tempFahrenheit", &fahrenheit, DOUBLE);
    pinMode(LED, OUTPUT); // Use for a simple test of the led on or off by subscribing to a topical called led
    pinMode(PIR, INPUT_PULLUP); // Initialize pin as input with an internal pull-up resistor
    ledTimer.start();
    attachInterrupt(PIR, motion_detected, RISING);
    Serial.begin(9600);
}

void loop() 
{
    if (millis() > DS18B20nextSampleTime)
    {
        getTemp();
    }

    if (millis() > MetricnextPublishTime)
    {
        Serial.println("Publishing now.");
        publishData();
    }
    
    if (millis() > PIRnextSampleTime)
    {
        if (alarmStatus == 1)
        {
            raiseAlarm();
        }
    }
}

void blink_led()
{
    digitalWrite(LED, !digitalRead(LED));
}

void motion_detected()
{
    alarmStatus = 1;
}

void resetAlarm()
{
    alarmStatus = 0;
}

void raiseAlarm()
{
    Particle.publish("photon/motion/detected", "1");
    resetAlarm();
    PIRnextSampleTime = millis() + PIR_SAMPLE_INTERVAL;
}

void publishData() 
{
    if(!ds18b20.crcCheck())
    {
        return;
    }
    Particle.publish("photon/temperature/celsius", String(celsius));
    Particle.publish("photon/temperature/fahrenheit", String(fahrenheit));
    MetricnextPublishTime = millis() + Metric_Publish_Rate;
}

void getTemp()
{
    if(!ds18b20.search())
    {
        ds18b20.resetsearch();
        celsius = ds18b20.getTemperature();
        Serial.println(celsius);
        while (!ds18b20.crcCheck() && dsAttempts < 4)
        {
            Serial.println("Caught bad value.");
            dsAttempts++;
            Serial.print("Attempts to Read: ");
            Serial.println(dsAttempts);
            if (dsAttempts == 3)
            {
                delay(1000);
            }
            ds18b20.resetsearch();
            celsius = ds18b20.getTemperature();
            continue;
        }
        dsAttempts = 0;
        fahrenheit = ds18b20.convertToFahrenheit(celsius);
        DS18B20nextSampleTime = millis() + DS18B20_SAMPLE_INTERVAL;
        Serial.println(fahrenheit);
    }
}

For example (another hypothetical here…)

Aquarium controller:

Pump speed can be controlled 0%-100% by Particle.function
Pump speed can be scheduled by Particlie.function from an array [ { time, speed }, { time, speed} ]
Pump schedule will persist and continue if connectivity is lost from the cloud.
Pump schedule will default if restarted and cloud connectivity is unavailable.
Pump speed can be read by Particle.variable.
When pump stops, heater is turned off.
Temperature can be set by Particle.function.
Temperature is persisted if connectivity is lost.
Temperature defaults if restarted and connectivity is unavailable. (Would be interesting to know if user defined value can persist a restart?)
Temperature is logged every minute.
Temperature must also be readable on demand Particle.variable.
Temperature change can be scheduled throughout the day by Particle.function.
Temperature schedule persists if connectivity is lost.
Ph readings are taken once an hour and logged.
Ph read can manually be triggered using a Particle.function and 2 Particle.variables (phValue and phLastRead)
Ph threshold can be set using Particle.function (max/min)
If Ph exceeds upper limit dose with 1 unit of acid and log dose time.
If Ph drops below lower limit dose with 1 unit of alkali and log dose time.
If connectivity is lost or it is reset then maintain this threshold.
Light intensity can be controlled by Particle.function.
Light intensity can be read by Particle.variable.
Light schedule can be set by Particle.function.
Light schedule persists reset and connectivity lost.
Feeding schedule is set by Particle.function.
Feeding can be triggered by Particle.function.
Last feed time is available as a Particle.variable.
Feeding can be triggered manually this will skip the next scheduled event.
Feed routine will bring the lights up full, stop the pump and consequently stop the heaters for 3 minutes.

This is a bigger example, there is some repetition of scenarios but I think it is a good illustration that perhaps people can relate to (if they keep fish) and it has an element of critical or reliable functioning.

Let say for a more realistic scenario that this is all controlled (in terms of Particle.function calls) by a web interface, so the UI makes the Particle.function call. Things like schedules and user defined settings like temperature, ph, etc have a factory default but are also set by the UI. When the user sets a parameter or schedule in the UI they are stored in a database and these values can be retrieved through a webhook which the particle will do when a schedule function is called or if the particle has lost connectivity or has been restarted.

So we have a fair bit going on in this scenario as I’m sure you can appreciate; we have 6 parameters or functions to control (pump, heating, ph, dosing pump, lighting and feeding), 5 schedules, 10 functions, 6 variables, 7 timers and 6 interrupts (I think).

We also want pin state on startup to be managed so pump and heater are on, lights off, feed off, dosing pump off.

I’m not expecting you to write this code for me as this is a learning exercise, but I would really appreicate if can you give me some pointers on where to begin, how to structure something as complex as this (in terms of managing all those timers, etc), best practices, patterns that could be used, etc?

Many thanks

Andy

Actually, I don’t think what you are trying to achieve requires this much complexity. You could just build a timeout into your ISR as easy as this:

void motion_detected()
{
  static volatile unsigned long lastFlag= 0;
  if(millis() - lastFlag > 5000)
  {
    isrFlag = true;
    lastFlag = millis();
  }
}

While the millis() function won’t advance during the ISR, you can call it, so you have a pretty robust timeout method for the interrupt directed motion sensor and you don’t need any funky multithreading or Class development overhead (for this quite trivial application).

Side note… I would caution you about unsigned addition… your timers may have the dreaded rollover problem.

You should consider using unsigned subtraction as so eloquently described here.

2 Likes

Also keep in mind you can only register 4 Particle.functions(). There is a multifunction implementation out there somewhere and you can override the max but they are costly in terms of resources from what I understand.

2 Likes

Uhh, that’s some use case there :wink:

I’d be happy to provide ideas, but I’m definetly not in the position to say this is right or that is wrong, so whatever I say is merely my personal opinion :wink:

Just some minor pointers (which took longer, so others got in quicker ;-)):

  • If you write const int LED = D7;, your variable will be placed in flash and safe RAM as well as initialisation code.
  • If you declare a function befor its first use, you don’t need to provide a prototype (int alarm(String command);).
  • I’ve learnt to never trust myself, so I always add a final unconditional return xxx; statement in my typed functions, to avoid errors if I’d happen to change an else into an else if() and hence might drop out of the function without properly returning something.
  • If you are living somewhere with daylight saving time, you might need to add some extra logic to Time.zone(-5);
  • You could use other Timers instead of your if (millis() ...) constructs, or even the TimeAlarms library for timed events
  • The commonly used practice for if (millis() ...) constructs is if (millis() - msLastEvent > msDelayTime) - the reasons are widely discussed on Arduino forums (as @BulldogLowell has already said)
  • If you want to achieve a dead-time for your PIR detector, you’d need to observe this inside the ISR rather than in loop(), otherwise the flag will still be set immediately and the alarm will still fire then after the dead-time (as @BulldogLowell has already said)
  • Since you have several independet timed actions running, you might need to take precautions that your code does never offend the Particle.publish() limit of avg 1/sec with a burst rate of 4, otherwise the last event(s) will not be sent and your device be muted for at least 4 sec (especially immediately after start all your timed events will fire, due to initial millis() > 0, and two system events are published upon cloud connect)
  • Mark variables that get set/altered by ISR or other out of order instructions (e.g. other threads) as volatile!
  • To care for loss of cloud connection have a look at non-AUTOMATIC SYSTEM_MODES() and SYSTEM_THREAD(ENABLED)
  • For persisting schedules have a look at the EEPROM “library”

If you still want to dive into multithreading with the Photon, have a read here
https://github.com/spark/firmware/pull/614

There are some posts (which I can’t find just now) how to create a user thread, but it’s rather tricky and very alpha :wink:

2 Likes

Just wanted to say thanks for all the replies, I will spend a little time tonight digesting the advice and see if I can apply it to my bloated use case. An aquarium controller is on my backlog of diy projects but it makes an interesting learning scenario due to the number of control parameters etc.