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