I'm trying to make a toilet sensorish trigger using vl53l0x sensor, I'm having trouble firing an action while my hand is in front of the sensor for 5 seconds or so, while I've tried different versions of blinkwithoutdelay sketches, and other methods found online, all of them, trigger the 5 seconds, after I pulled my hand off the sensor, which is not what I want. Thanks in advance, I posted my sketch to what I got so far. Thanks in advance!
// Library for TOF SENSOR
#include <Wire.h>
#include <VL53L0X.h>
VL53L0X sensor;
// Time calculation
unsigned long startTime;
unsigned long endTime; // store end time here
unsigned long duration; // duration stored
byte timerRunning;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Wire.begin();
sensor.init();
sensor.setTimeout(500);
// Start continuous back-to-back mode (take readings as
// fast as possible). To use continuous timed mode
// instead, provide a desired inter-measurement period in
// ms (e.g. sensor.startContinuous(100)).
sensor.startContinuous();
}
void loop() {
// put your main code here, to run repeatedly:
delay(1000);
int tofdata = sensor.readRangeContinuousMillimeters();
int distance = tofdata / 10; // convert mm to cm
Serial.print( distance ); // print new converted data
Serial.println( " cm" );
// Code for presence detection
if ( timerRunning == 0 && distance <= 20 ){
startTime = millis() / 1000;
Serial.println("time started, starting count");
timerRunning = 1;
}
if ( timerRunning == 1 && distance >= 20 ){
endTime = millis() / 1000;
timerRunning = 0;
duration = endTime - startTime;
Serial.println ("Presence detected for seconds: ");
Serial.print(duration);
}
}
If want it to fire after the hand has been in front of the sensor for 5 seconds try this:
void loop() {
// Get distance
delay(1000);
int tofdata = sensor.readRangeContinuousMillimeters();
int distance = tofdata / 10; // convert mm to cm
// Code for presence detection
if (distance <= 20 ) {
// Object is close, check if timer is running
if (!timerRunning) {
// Timer not running. Start timer
startTime = millis();
Serial.println("time started, starting count");
timerRunning = 1;
}
else {
// Has 5 seconds passed?
uint32_t elapsed_time = millis() - startTime ;
if (elapsed_time >= 5000) {
// YES. DO SOMETHING HERE
// and reset
timerRunning = 0;
}
}
}
else {
// Object is not close.
timerRunning = 0;
}
Related
I am creating this temp monitoring system...Therefore, i want to get messages/alerts when the temperature are below 6 and again when they come back to above 6. Note: I don't want the alert (sendMailNormalValue():wink: to come when the system boots up....How do I deactivate the sendMailNormalValue(); and activate it only when the temp are below 6 (only to alert me when comes back to above 6)..
#include <OneWire.h>
#include <DallasTemperature.h>
// GPIO where the DS18B20 is connected to
const int oneWireBus = 5;
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
//========================================
float t;
int period = 30000;
unsigned long time_now = 0;
bool DisplayFirstTime = true;
int period1 = 30000;
unsigned long time_now1 = 0;
void sendMailBelowValue(){
}
void sendMailNormalValue(){
}
void setup() {
// put your setup code here, to run once:
Serial.begin (9600);
}
void loop() {
// put your main code here, to run repeatedly:
sensors.requestTemperatures();
t = sensors.getTempCByIndex(0);
float p=t-2;
// Check if any reads failed and exit early (to try again).
if (isnan(t)) {
Serial.println("Failed to read from sensor !");
delay(500);
return;
}
if (t>6){
if(millis() > time_now + period){
time_now = millis();
sendMailNormalValue();
Serial.print ("You got messagae");
}
}
if (t<6){
if(millis() > time_now1 + period1 || DisplayFirstTime){
DisplayFirstTime = false;
time_now = millis();
sendMailBelowValue();
}
}
}
I think I understand what you mean. On error (temp < 6), you want to send an email every 30 seconds; when the teperature reaches 6 or mode, send a single email to say the error condition has been fixed. On boot, only send an email if in error.
If that's it, you'll need to keep track of the error condition using a global flag.
// ...
bool tempError; // initialized in setup()
void setup()
{
// ...
float t;
for(;;) // loop until we get a valid reading.
{
t = sensors.getTempCByIndex(0);
if (!isnan(t))
break;
Serial.println("Failed to read from sensor !");
delay(500);
}
tempError = (t < 6);
}
void loop()
{
// read temp and check sensor...
// ...
if (t < 6)
{
tempError = true;
// send error email, set timer, etc...
}
else if (tempError)
{
// temp is now fine, after being too low.
tempError = false; // Clear error flag.
// send OK email, only once.
// Don't forget to clear the 30 second email timer !!!
}
}
Usually, you'd want some hysteresis in an alert system. You should consider waiting some seconds after the temperature has been 'fixed' before sending the 'OK' email. Otherwise you may end up getting lots of fail/fixed emails when the temperature is around 6 degrees. This is usually done with a simple state machine engine, but that is a bit beyond the scope of this questkon.
I bought the same boards used in this video on Youtube (the componts are THIS geiger counter (that I am pretty sure its working, the tiks on the speacker are coerent) and THIS esp (I don't know if is here the problem or in the code), I loaded the arduino code into the esp32 board, but the display remains stuck on "Measuring". It connects correctly to the ThingSpeak channel, but always linearly increasing measurements. The number grows continuously (view the attached image). I tried to insert some currentMillis and previousMillis println. Both variables are always 0 in the serial monitor.
So I tried adding currentMillis = millis (); at the beginning of the loop (), at this point the display has finally shown "radioactivity" with the usual increasing numbers. Even if the card is disconnected from the geiger counter, the data are the same and always increasing. How can I solve it?
#define PRINT_DEBUG_MESSAGES
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <IFTTTMaker.h>
#include <ThingSpeak.h>
#include <SSD1306.h>
//#include <credentials.h> // or define mySSID and myPASSWORD and THINGSPEAK_API_KEY
#define LOG_PERIOD 20000 //Logging period in milliseconds
#define MINUTE_PERIOD 60000
#define WIFI_TIMEOUT_DEF 30
#define PERIODE_THINKSPEAK 20000
#ifndef CREDENTIALS
// WLAN
#define mySSID ""
#define myPASSWORD ""
//IFTT
#define IFTTT_KEY "......."
// Thingspeak
#define SECRET_CH_ID 000000 // replace 0000000 with your channel number
#define SECRET_WRITE_APIKEY "XYZ" // replace XYZ with your channel write API Key
#endif
// IFTTT
#define EVENT_NAME "Radioactivity" // Name of your event name, set when you are creating the applet
IPAddress ip;
WiFiClient client;
WiFiClientSecure secure_client;
IFTTTMaker ifttt(IFTTT_KEY, secure_client);
SSD1306 display(0x3c, 5, 4);
volatile unsigned long counts = 0; // Tube events
unsigned long cpm = 0; // CPM
unsigned long previousMillis; // Time measurement
const int inputPin = 7;
unsigned int thirds = 0;
unsigned long minutes = 1;
unsigned long start = 0;
unsigned long entryThingspeak;
unsigned long currentMillis = millis();
unsigned long myChannelNumber = SECRET_CH_ID;
const char * myWriteAPIKey = SECRET_WRITE_APIKEY;
#define LOG_PERIOD 20000 //Logging period in milliseconds
#define MINUTE_PERIOD 60000
void ISR_impulse() { // Captures count of events from Geiger counter board
counts++;
}
void displayInit() {
display.init();
display.flipScreenVertically();
display.setFont(ArialMT_Plain_24);
}
void displayInt(int dispInt, int x, int y) {
display.setColor(WHITE);
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(x, y, String(dispInt));
display.setFont(ArialMT_Plain_24);
display.display();
}
void displayString(String dispString, int x, int y) {
display.setColor(WHITE);
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(x, y, dispString);
display.setFont(ArialMT_Plain_24);
display.display();
}
/****reset***/
void software_Reset() // Restarts program from beginning but does not reset the peripherals and registers
{
Serial.print("resetting");
esp_restart();
}
void IFTTT(String event, int postValue) {
if (ifttt.triggerEvent(EVENT_NAME, String(postValue))) {
Serial.println("Successfully sent to IFTTT");
} else
{
Serial.println("IFTTT failed!");
}
}
void postThingspeak( int value) {
// Write to ThingSpeak. There are up to 8 fields in a channel, allowing you to store up to 8 different
// pieces of information in a channel. Here, we write to field 1.
int x = ThingSpeak.writeField(myChannelNumber, 1, value, myWriteAPIKey);
if (x == 200) {
Serial.println("Channel update successful.");
}
else {
Serial.println("Problem updating channel. HTTP error code " + String(x));
}
}
void setup() {
Serial.begin(115200);
displayInit();
ThingSpeak.begin(client); // Initialize ThingSpeak
displayString("Welcome", 64, 15);
Serial.println("Connecting to Wi-Fi");
WiFi.begin(mySSID, myPASSWORD);
int wifi_loops = 0;
int wifi_timeout = WIFI_TIMEOUT_DEF;
while (WiFi.status() != WL_CONNECTED) {
wifi_loops++;
Serial.print(".");
delay(500);
if (wifi_loops > wifi_timeout)
{
software_Reset();
}
}
Serial.println();
Serial.println("Wi-Fi Connected");
display.clear();
displayString("Measuring", 64, 15);
pinMode(inputPin, INPUT); // Set pin for capturing Tube events
interrupts(); // Enable interrupts
attachInterrupt(digitalPinToInterrupt(inputPin), ISR_impulse, FALLING); // Define interrupt on falling edge
unsigned long clock1 = millis();
start = clock1;
}
void loop() {
if (WiFi.status() != WL_CONNECTED)
{
software_Reset();
}
if (currentMillis - previousMillis > LOG_PERIOD) {
previousMillis = currentMillis;
cpm = counts * MINUTE_PERIOD / LOG_PERIOD;
//cpm=105;
counts = 0;
display.clear();
displayString("Radioactivity", 64, 0);
displayInt(cpm, 64, 30);
if (cpm > 100 ) IFTTT( EVENT_NAME, cpm);
}
// Serial.print("minutes: ");
// Serial.println(String(minutes));
//cpm = counts * MINUTE_PERIOD / LOG_PERIOD; this is just counts times 3 so:
cpm = counts / minutes;
if (millis() - entryThingspeak > PERIODE_THINKSPEAK) {
Serial.print("Total clicks since start: ");
Serial.println(String(counts));
Serial.print("Rolling CPM: ");
Serial.println(String(cpm));
postThingspeak(cpm);
entryThingspeak = millis();
}
// if ( thirds > 2) {
// counts = 0;
// thirds = 0;
// }
}
This is based on this work on GITHUB
Your code never updates the value of currentMillis. You set it equal to millis at global scope, likely before init is called and before millis even has a value and then you just leave it with that value. You're not telling that variable to call millis every time, you're just giving it the value of millis at that one instant. You need a line in loop to update that variable.
Because of that, this section never runs:
if (currentMillis - previousMillis > LOG_PERIOD) {
previousMillis = currentMillis;
cpm = counts * MINUTE_PERIOD / LOG_PERIOD;
//cpm=105;
counts = 0;
display.clear();
displayString("Radioactivity", 64, 0);
displayInt(cpm, 64, 30);
if (cpm > 100 ) IFTTT( EVENT_NAME, cpm);
}
and counts never gets set back to 0. That's why you see an ever increasing number.
You're also using this minutes variable as if it will count something, but you never add to it. So it just stays at 1 forever as far as I can tell.
tldr; what is an easy/logical way (for a beginner) to calculate BPM using pulse sensor and mkr1000? I don't want any visualizations or processing sketch, but just print BPM values
Please bear with me, I am a newbie at this and i've tried my best to understand this and fix this issue, but in vain.
I am using the pulse sensor (SEN-11574) with Arduino mkr1000 to calculate the BPM and print it in serial monitor. I was able to get raw readings using their starter code
// Variables
int PulseSensorPurplePin = 0; // Pulse Sensor PURPLE WIRE connected to ANALOG PIN 0
int LED13 = 13; // The on-board Arduion LED
int Signal; // holds the incoming raw data. Signal value can range from 0-1024
int Threshold = 550; // Determine which Signal to "count as a beat", and which to ingore.
// The SetUp Function:
void setup() {
pinMode(LED13,OUTPUT); // pin that will blink to your heartbeat!
Serial.begin(9600); // Set's up Serial Communication at certain speed.
}
// The Main Loop Function
void loop() {
Signal = analogRead(PulseSensorPurplePin); // Read the PulseSensor's value.
// Assign this value to the "Signal" variable.
Serial.println(Signal); // Send the Signal value to Serial Plotter.
if(Signal > Threshold){ // If the signal is above "550", then "turn-on" Arduino's on-Board LED.
digitalWrite(LED13,HIGH);
} else {
digitalWrite(LED13,LOW); // Else, the sigal must be below "550", so "turn-off" this LED.
}
delay(10);
}
However the real problem is that I am unable to calculate the BPM using their example code available on their website here
From what I understand, the interrupt timer function in the Interrupt.ino file is not compatible with mkr1000. Attached is this code for your reference.
// THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE.
// Timer 2 makes sure that we take a reading every 2 miliseconds
ISR(TIMER2_COMPA_vect){ // triggered when Timer2 counts to 124
cli(); // disable interrupts while we do this
Signal = analogRead(pulsePin); // read the Pulse Sensor
sampleCounter += 2; // keep track of the time in mS with this variable
int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise
// find the peak and trough of the pulse wave
if(Signal < thresh && N > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI
if (Signal < T){ // T is the trough
T = Signal; // keep track of lowest point in pulse wave
}
}
if(Signal > thresh && Signal > P){ // thresh condition helps avoid noise
P = Signal; // P is the peak
} // keep track of highest point in pulse wave
// NOW IT'S TIME TO LOOK FOR THE HEART BEAT
// signal surges up in value every time there is a pulse
if (N > 250){ // avoid high frequency noise
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){
Pulse = true; // set the Pulse flag when we think there is a pulse
digitalWrite(blinkPin,HIGH); // turn on pin 13 LED
IBI = sampleCounter - lastBeatTime; // measure time between beats in mS
lastBeatTime = sampleCounter; // keep track of time for next pulse
if(secondBeat){ // if this is the second beat, if secondBeat == TRUE
secondBeat = false; // clear secondBeat flag
for(int i=0; i<=9; i++){ // seed the running total to get a realisitic BPM at startup
rate[i] = IBI;
}
}
if(firstBeat){ // if it's the first time we found a beat, if firstBeat == TRUE
firstBeat = false; // clear firstBeat flag
secondBeat = true; // set the second beat flag
sei(); // enable interrupts again
return; // IBI value is unreliable so discard it
}
// keep a running total of the last 10 IBI values
word runningTotal = 0; // clear the runningTotal variable
for(int i=0; i<=8; i++){ // shift data in the rate array
rate[i] = rate[i+1]; // and drop the oldest IBI value
runningTotal += rate[i]; // add up the 9 oldest IBI values
}
rate[9] = IBI; // add the latest IBI to the rate array
runningTotal += rate[9]; // add the latest IBI to runningTotal
runningTotal /= 10; // average the last 10 IBI values
BPM = 60000/runningTotal; // how many beats can fit into a minute? that's BPM!
QS = true; // set Quantified Self flag
// QS FLAG IS NOT CLEARED INSIDE THIS ISR
}
}
if (Signal < thresh && Pulse == true){ // when the values are going down, the beat is over
digitalWrite(blinkPin,LOW); // turn off pin 13 LED
Pulse = false; // reset the Pulse flag so we can do it again
amp = P - T; // get amplitude of the pulse wave
thresh = amp/2 + T; // set thresh at 50% of the amplitude
P = thresh; // reset these for next time
T = thresh;
}
if (N > 2500){ // if 2.5 seconds go by without a beat
thresh = 530; // set thresh default
P = 512; // set P default
T = 512; // set T default
lastBeatTime = sampleCounter; // bring the lastBeatTime up to date
firstBeat = true; // set these to avoid noise
secondBeat = false; // when we get the heartbeat back
}
sei(); // enable interrupts when youre done!
}// end isr
On the interrupt-notes file they mention another work-around for processors that are not compatible with this code, but even after hours of following the intructions, the code didn't work, again with errors with timer interrupt functions.
Next, I used this guide but again, it didn't work either and just prints raw signal value that constantly changes (S1023). The code is attached (2 tabs):
/* Pulse Sensor Amped 1.4 by Joel Murphy and Yury Gitman http://www.pulsesensor.com
Adapted by sdizdarevic
---------------------- Notes ---------------------- ----------------------
This code:
1) Blinks an LED to User's Live Heartbeat PIN 6
2) Fades an LED to User's Live HeartBeat
3) Determines BPM
4) Prints All of the Above to Serial
Read Me:
https://github.com/WorldFamousElectronics/PulseSensor_Amped_Arduino/blob/master/README.md
---------------------- ---------------------- ----------------------
*/
// Variables
int pulsePin = 0; // Pulse Sensor purple wire connected to analog pin 0
int blinkPin = 6; // pin to blink led at each beat
//int fadePin = 5; // pin to do fancy classy fading blink at each beat
//int fadeRate = 0; // used to fade LED on with PWM on fadePin
// Volatile Variables, used in the interrupt service routine!
volatile int BPM; // int that holds raw Analog in 0. updated every 2mS
volatile int Signal; // holds the incoming raw data
volatile int IBI = 600; // int that holds the time interval between beats! Must be seeded!
volatile boolean Pulse = false; // "True" when User's live heartbeat is detected. "False" when not a "live beat".
volatile boolean QS = false; // becomes true when Arduoino finds a beat.
volatile int rate[10]; // array to hold last ten IBI values
volatile unsigned long sampleCounter = 0; // used to determine pulse timing
volatile unsigned long lastBeatTime = 0; // used to find IBI
volatile int P =512; // used to find peak in pulse wave, seeded
volatile int T = 512; // used to find trough in pulse wave, seeded
volatile int thresh = 525; // used to find instant moment of heart beat, seeded
volatile int amp = 100; // used to hold amplitude of pulse waveform, seeded
volatile boolean firstBeat = true; // used to seed rate array so we startup with reasonable BPM
volatile boolean secondBeat = false; // used to seed rate array so we startup with reasonable BPM
// Regards Serial OutPut -- Set This Up to your needs
static boolean serialVisual = false; // Set to 'false' by Default. Re-set to 'true' to see Arduino Serial Monitor ASCII Visual Pulse
void setup(){
pinMode(blinkPin,OUTPUT); // pin that will blink to your heartbeat!
//pinMode(fadePin,OUTPUT); // pin that will fade to your heartbeat!
Serial.begin(115200); // we agree to talk fast!
//interruptSetup(); // sets up to read Pulse Sensor signal every 2mS
// IF YOU ARE POWERING The Pulse Sensor AT VOLTAGE LESS THAN THE BOARD VOLTAGE,
// UN-COMMENT THE NEXT LINE AND APPLY THAT VOLTAGE TO THE A-REF PIN
// analogReference(EXTERNAL);
}
// Where the Magic Happens
void loop(){
//
//
Signal = analogRead(pulsePin); // read the Pulse Sensor
sampleCounter += 2; // keep track of the time in mS with this variable
int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise
// find the peak and trough of the pulse wave
if(Signal < thresh && N > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI
if (Signal < T){ // T is the trough
T = Signal; // keep track of lowest point in pulse wave
}
}
if(Signal > thresh && Signal > P){ // thresh condition helps avoid noise
P = Signal; // P is the peak
} // keep track of highest point in pulse wave
// NOW IT'S TIME TO LOOK FOR THE HEART BEAT
// signal surges up in value every time there is a pulse
if (N > 250){ // avoid high frequency noise
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){
Pulse = true; // set the Pulse flag when we think there is a pulse
digitalWrite(blinkPin,HIGH); // turn on pin 13 LED
IBI = sampleCounter - lastBeatTime; // measure time between beats in mS
lastBeatTime = sampleCounter; // keep track of time for next pulse
if(secondBeat){ // if this is the second beat, if secondBeat == TRUE
secondBeat = false; // clear secondBeat flag
for(int i=0; i<=9; i++){ // seed the running total to get a realisitic BPM at startup
rate[i] = IBI;
}
}
if(firstBeat){ // if it's the first time we found a beat, if firstBeat == TRUE
firstBeat = false; // clear firstBeat flag
secondBeat = true; // set the second beat flag
return; // IBI value is unreliable so discard it
}
// keep a running total of the last 10 IBI values
word runningTotal = 0; // clear the runningTotal variable
for(int i=0; i<=8; i++){ // shift data in the rate array
rate[i] = rate[i+1]; // and drop the oldest IBI value
runningTotal += rate[i]; // add up the 9 oldest IBI values
}
rate[9] = IBI; // add the latest IBI to the rate array
runningTotal += rate[9]; // add the latest IBI to runningTotal
runningTotal /= 10; // average the last 10 IBI values
BPM = 60000/runningTotal; // how many beats can fit into a minute? that's BPM!
QS = true; // set Quantified Self flag
// QS FLAG IS NOT CLEARED INSIDE THIS ISR
}
}
if (Signal < thresh && Pulse == true){ // when the values are going down, the beat is over
digitalWrite(blinkPin,LOW); // turn off pin 13 LED
Pulse = false; // reset the Pulse flag so we can do it again
amp = P - T; // get amplitude of the pulse wave
thresh = amp/2 + T; // set thresh at 50% of the amplitude
P = thresh; // reset these for next time
T = thresh;
}
if (N > 2500){ // if 2.5 seconds go by without a beat
thresh = 512; // set thresh default
P = 512; // set P default
T = 512; // set T default
lastBeatTime = sampleCounter; // bring the lastBeatTime up to date
firstBeat = true; // set these to avoid noise
secondBeat = false; // when we get the heartbeat back
}
serialOutput() ;
if (QS == true){ // A Heartbeat Was Found
// BPM and IBI have been Determined
// Quantified Self "QS" true when arduino finds a heartbeat
// fadeRate = 255; // Makes the LED Fade Effect Happen
// Set 'fadeRate' Variable to 255 to fade LED with pulse
serialOutputWhenBeatHappens(); // A Beat Happened, Output that to serial.
QS = false; // reset the Quantified Self flag for next time
}
// ledFadeToBeat(); // Makes the LED Fade Effect Happen
delay(20); // take a break
}
/*void ledFadeToBeat(){
fadeRate -= 15; // set LED fade value
fadeRate = constrain(fadeRate,0,255); // keep LED fade value from going into negative numbers!
//analogWrite(fadePin,fadeRate); // fade LED
}
*/
SerialHandling file:
//////////
///////// All Serial Handling Code,
///////// It's Changeable with the 'serialVisual' variable
///////// Set it to 'true' or 'false' when it's declared at start of code.
/////////
void serialOutput(){ // Decide How To Output Serial.
if (serialVisual == true){
arduinoSerialMonitorVisual('-', Signal); // goes to function that makes Serial Monitor Visualizer
} else{
sendDataToSerial('S', Signal); // goes to sendDataToSerial function
}
}
// Decides How To OutPut BPM and IBI Data
void serialOutputWhenBeatHappens(){
if (serialVisual == true){ // Code to Make the Serial Monitor Visualizer Work
Serial.print("*** Heart-Beat Happened *** "); //ASCII Art Madness
Serial.print("BPM: ");
Serial.print(BPM);
Serial.print(" ");
} else{
sendDataToSerial('B',BPM); // send heart rate with a 'B' prefix
sendDataToSerial('Q',IBI); // send time between beats with a 'Q' prefix
}
}
// Sends Data to Pulse Sensor Processing App, Native Mac App, or Third-party Serial Readers.
void sendDataToSerial(char symbol, int data ){
Serial.print(symbol);
Serial.println(data);
}
// Code to Make the Serial Monitor Visualizer Work
void arduinoSerialMonitorVisual(char symbol, int data ){
const int sensorMin = 0; // sensor minimum, discovered through experiment
const int sensorMax = 1024; // sensor maximum, discovered through experiment
int sensorReading = data;
// map the sensor range to a range of 12 options:
int range = map(sensorReading, sensorMin, sensorMax, 0, 11);
// do something different depending on the
// range value:
switch (range) {
case 0:
Serial.println(""); /////ASCII Art Madness
break;
case 1:
Serial.println("---");
break;
case 2:
Serial.println("------");
break;
case 3:
Serial.println("---------");
break;
case 4:
Serial.println("------------");
break;
case 5:
Serial.println("--------------|-");
break;
case 6:
Serial.println("--------------|---");
break;
case 7:
Serial.println("--------------|-------");
break;
case 8:
Serial.println("--------------|----------");
break;
case 9:
Serial.println("--------------|----------------");
break;
case 10:
Serial.println("--------------|-------------------");
break;
case 11:
Serial.println("--------------|-----------------------");
break;
}
}
Serial monitor only displays these numbers that are constantly changing:
S797
S813
S798
S811
S822
S802
S821
S819
S818
S806
S797
S797
S812
S816
S794
S820
S821
S808
S816
S820
S803
S810
S811
S806
S822
S817
S811
S822
S800
S820
S799
S800
S815
S809
S820
S822
S821
S809
S796
S821
S816
S798
S820
All in all, I was hoping if someone could help me with the code to calculate BPM in a more basic/ easy manner without having to deal with visualization of the BPM.
Sorry for the long post, thanks!
This is how i did it, to overpass the absence of interrupt on my board:
#define pulsePin A0
// VARIABLES
int rate[10];
unsigned long sampleCounter = 0;
unsigned long lastBeatTime = 0;
unsigned long lastTime = 0, N;
int BPM = 0;
int IBI = 0;
int P = 512;
int T = 512;
int thresh = 512;
int amp = 100;
int Signal;
boolean Pulse = false;
boolean firstBeat = true;
boolean secondBeat = true;
boolean QS = false;
void setup() {
Serial.begin(9600);
}
void loop() {
if (QS == true) {
Serial.println("BPM: "+ String(BPM));
QS = false;
} else if (millis() >= (lastTime + 2)) {
readPulse();
lastTime = millis();
}
}
void readPulse() {
Signal = analogRead(pulsePin);
sampleCounter += 2;
int N = sampleCounter - lastBeatTime;
detectSetHighLow();
if (N > 250) {
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI / 5) * 3) )
pulseDetected();
}
if (Signal < thresh && Pulse == true) {
Pulse = false;
amp = P - T;
thresh = amp / 2 + T;
P = thresh;
T = thresh;
}
if (N > 2500) {
thresh = 512;
P = 512;
T = 512;
lastBeatTime = sampleCounter;
firstBeat = true;
secondBeat = true;
}
}
void detectSetHighLow() {
if (Signal < thresh && N > (IBI / 5) * 3) {
if (Signal < T) {
T = Signal;
}
}
if (Signal > thresh && Signal > P) {
P = Signal;
}
}
void pulseDetected() {
Pulse = true;
IBI = sampleCounter - lastBeatTime;
lastBeatTime = sampleCounter;
if (firstBeat) {
firstBeat = false;
return;
}
if (secondBeat) {
secondBeat = false;
for (int i = 0; i <= 9; i++) {
rate[i] = IBI;
}
}
word runningTotal = 0;
for (int i = 0; i <= 8; i++) {
rate[i] = rate[i + 1];
runningTotal += rate[i];
}
rate[9] = IBI;
runningTotal += rate[9];
runningTotal /= 10;
BPM = 60000 / runningTotal;
QS = true;
}
The sensor I used is a DFRobot Piezo Disc Vibration Sensor Module.
void setup() {
Serial.begin(57600);
}
void loop() {
int avg = 0;
for(int i=0;i<64;i++){
avg+=analogRead(A2);
}
Serial.println(avg/64,DEC);
delay(5);
}
void setup() {
Serial.begin(57600);
}
void loop() {
int avg = 0;
for(int i=0;i<64;i++){
avg+=analogRead(A2);
}
Serial.println(avg/64,DEC);
delay(5);
}
When defining an arbitrary threshold (e.g. half of the maximum measured value), the rising edge of the signal will pass the threshold once per heartbeat, making measuring it as simple as measuring the time between two successive beats. For less jitter, I chose to calculate the heart rate using the average of the last 16 time differences between the beats.
code that calculates the heart rate and outputs the average heart rate over the last 16 beats at every beat:
int threshold = 60;
int oldvalue = 0;
int newvalue = 0;
unsigned long oldmillis = 0;
unsigned long newmillis = 0;
int cnt = 0;
int timings[16];
void setup() {
Serial.begin(57600);
}
void loop() {
oldvalue = newvalue;
newvalue = 0;
for(int i=0; i<64; i++){ // Average over 16 measurements
newvalue += analogRead(A2);
}
newvalue = newvalue/64;
// find triggering edge
if(oldvalue<threshold && newvalue>=threshold){
oldmillis = newmillis;
newmillis = millis();
// fill in the current time difference in ringbuffer
timings[cnt%16]= (int)(newmillis-oldmillis);
int totalmillis = 0;
// calculate average of the last 16 time differences
for(int i=0;i<16;i++){
totalmillis += timings[i];
}
// calculate heart rate
int heartrate = 60000/(totalmillis/16);
Serial.println(heartrate,DEC);
cnt++;
}
delay(5);
}
int threshold = 60;
int oldvalue = 0;
int newvalue = 0;
unsigned long oldmillis = 0;
unsigned long newmillis = 0;
int cnt = 0;
int timings[16];
void setup() {
Serial.begin(57600);
}
void loop() {
oldvalue = newvalue;
newvalue = 0;
for(int i=0; i<64; i++){ // Average over 16 measurements
newvalue += analogRead(A2);
}
newvalue = newvalue/64;
// find triggering edge
if(oldvalue<threshold && newvalue>=threshold){
oldmillis = newmillis;
newmillis = millis();
// fill in the current time difference in ringbuffer
timings[cnt%16]= (int)(newmillis-oldmillis);
int totalmillis = 0;
// calculate average of the last 16 time differences
for(int i=0;i<16;i++){
totalmillis += timings[i];
}
// calculate heart rate
int heartrate = 60000/(totalmillis/16);
Serial.println(heartrate,DEC);
cnt++;
}
delay(5);
}
If you would like to try this at home, just connect the analog output of the sensor to A2 (or change the code) and connect the 5V and GND lines of the sensor.
I wrote the following codes in Arduino uno with the header file TinyGPSPlus,and uses GPS SKG 13BL(GPS module).
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
/*
This program sketch obtain and print the lati,logi,speed,date and time
It requires the use of SoftwareSerial, and assumes that you have a
9600-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
*/
static const int RXPin = 4, TXPin = 3;
static const uint32_t GPSBaud = 9600;
// The TinyGPS++ object
TinyGPSPlus gps;
// The serial connection to the GPS device
SoftwareSerial ss(RXPin, TXPin);
void setup()
{
Serial.begin(9600);
ss.begin(GPSBaud);
Serial.println(F("GPS LOADING....."));
Serial.println(F("Obtain and print lati,logi,speed,date and time"));
Serial.println(F("Testing by : "));
Serial.println(F("Billa"));
Serial.println();
}
void loop()
{
// This sketch displays information every time a new sentence is correctly encoded.
while (ss.available() > 0)
if (gps.encode(ss.read()))
displayInfo();
if (millis() > 5000 && gps.charsProcessed() < 10)
{
Serial.println(F("No GPS detected: check wiring."));
while(true);
}
}
void displayInfo()
{
Serial.print(F("Location: "));
if (gps.location.isValid())
{
Serial.print(gps.location.lat(), 6);
Serial.print(F(","));
Serial.print(gps.location.lng(), 6);
}
else
{
Serial.print(F("INVALID"));
}
Serial.print(F(" Speed: "));
if (gps.speed.isValid())
{
Serial.print(gps.speed.kmph());
Serial.print(F(" KMPH "));
}
else
{
Serial.print(F("INVALID"));
}
Serial.print(F(" Date : "));
if (gps.date.isValid())
{
Serial.print(gps.date.month());
Serial.print(F("/"));
Serial.print(gps.date.day());
Serial.print(F("/"));
Serial.print(gps.date.year());
}
else
{
Serial.print(F("INVALID"));
}
Serial.print(F(" Time : "));
if (gps.time.isValid())
{
int hour= gps.time.hour() + 5;
if (hour < 10) Serial.print(F("0"));
if(hour > 12) hour-=11;
Serial.print(hour);
Serial.print(F(":"));
int minute = gps.time.minute() + 30;
if(minute >= 60) minute-=60;
if (minute < 10) Serial.print(F("0"));
Serial.print(minute);
Serial.print(F(":"));
if (gps.time.second() < 10) Serial.print(F("0"));
Serial.print(gps.time.second());
}
else
{
Serial.print(F("INVALID"));
}
Serial.println();
}
It obtained the required output.Displays the lines of data continusly on serial monitor.
But now i need to get these data exactly at every 5 secs (i.e At every 5 Secs the above code should generate output as per that instant).I tried to do this using delay and rewrote the loop code as follows
void loop()
{
delay(5000);
// This sketch displays information every time a new sentence is correctly encoded.
while (ss.available() > 0)
if (gps.encode(ss.read()))
displayInfo();
if (millis() > 5000 && gps.charsProcessed() < 10)
{
Serial.println(F("No GPS detected: check wiring."));
while(true);
}
}
But this doesnt obtained the output as desired.Can anyone please help me to solve this.Where should i edit and what changes should i make.
This is EXACTLY why I wrote NeoGPS. The example programs for all other libraries are just not structured properly. I'm looking at you, smartDelay()...
NeoGPS is structured around receiving a complete fix from the GPS device. This usually requires several sentences to be received. Other GPS libraries only tell you when one sentence is received. Furthermore, it's difficult to tell if two sentences are from the same 1-second update interval, or two consecutive intervals.
You want to display information once every 5 seconds, but that may be once every 20 sentences. And according to the Arduino millis() clock, it will be about 5000ms, but not exactly. millis() will drift against the GPS interval, depending on how accurate your crystal is. The GPS interval is very accurate,to the limits of the atomic clock, serial baud rate, and GPS device calculation time.
Here is your sketch, modified to use NeoGPS:
#include <NMEAGPS.h>
/*
This program sketch obtain and print the lati,logi,speed,date and time
It requires the use of SoftwareSerial, and assumes that you have a
9600-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
*/
#include <NeoSWSerial.h>
static const int RXPin = 4, TXPin = 3;
NeoSWSerial gpsPort(RXPin, TXPin);
static const uint32_t GPSBaud = 9600;
NMEAGPS gps;
gps_fix fix;
uint8_t fixCount = 0;
void setup()
{
Serial.begin(9600);
gpsPort.begin(GPSBaud);
Serial.println(F("GPS LOADING....."));
Serial.println(F("Obtain and print lati,logi,speed,date and time"));
Serial.println(F("Testing by : "));
Serial.println(F("Billa"));
Serial.println();
}
void loop()
{
while (gps.available( gpsPort )) {
fix = gps.read();
// Once every 5 seconds...
if (++fixCount >= 5) {
displayInfo();
fixCount = 0;
}
}
if ((gps.statistics.chars < 10) && (millis() > 5000)) {
Serial.println( F("No GPS detected: check wiring.") );
while(true);
}
}
void displayInfo()
{
Serial.print(F("Location: "));
if (fix.valid.location) {
Serial.print( fix.latitude(), 5 );
Serial.print( ',' );
Serial.print( fix.longitude(), 5 );
} else {
Serial.print(F("INVALID"));
}
Serial.print(F(" Speed: "));
if (fix.valid.speed) {
Serial.print(fix.speed_kph());
Serial.print(F(" KMPH "));
} else {
Serial.print(F("INVALID"));
}
// Shift the date/time to local time
NeoGPS::clock_t localSeconds;
NeoGPS::time_t localTime;
if (fix.valid.date && fix.valid.time) {
using namespace NeoGPS; // save a little typing below...
localSeconds = (clock_t) fix.dateTime; // convert structure to a second count
localSeconds += 5 * SECONDS_PER_HOUR + 30 * SECONDS_PER_MINUTE; // shift timezone
localTime = localSeconds; // convert back to a structure
}
Serial.print(F(" Date : "));
if (fix.valid.date) {
Serial.print(localTime.month);
Serial.print('/');
Serial.print(localTime.date);
Serial.print('/');
Serial.print(localTime.year);
} else {
Serial.print(F("INVALID"));
}
Serial.print(F(" Time : "));
if (fix.valid.time) {
Serial.print(localTime.hours);
Serial.print(':');
if (localTime.minutes < 10) Serial.print('0');
Serial.print(localTime.minutes);
Serial.print(':');
if (localTime.seconds < 10) Serial.print(F("0"));
Serial.print(localTime.seconds);
} else {
Serial.print(F("INVALID"));
}
Serial.println();
}
This sketch displays every fifth fix, without using the inaccurate millis(), or the nasty delay(). Ain't nobody got time for dat!
And each fix is accumuated from all the sentences in each 1-second interval, whether it's one sentence (RMC?), or 8 sentences (GGA, GLL, RMC, GSV*3, GSA and VTG). In NeoGPS, counting fixes is equivalent to counting seconds.
NOTE: if you need a super-accurate 5-second interval, consider using the PPS pin, if available.
The local time is CORRECTLY computed in this sketch, even if the timezone shift steps into a new hour, day, month or year. Your sketch does not cross day boundaries correctly. 5.5 hour shift, if I read your sketch correctly.
Should I mention NeoGPS is smaller, faster and more accurate than all other libraries? :) It's available from the Arduino IDE Library Manager, under the menu Sketch -> Include Library -> Manage Libraries.
You should also consider using something besides SoftwareSerial. It is very inefficient, because it disables interrupts for long periods of time. This can interfere with other parts of your sketch or with other libraries.
The best software serial port library is AltSoftSerial. If you can switch to pins 8 & 9, I would strongly recommend doing so.
If you can't switch pins (are you really sure?), you should use my NeoSWSerial. It works on any two pins, and is almost as efficient. It supports the 9600 baud rate you are using.
You should avoid using the normal delay function when working with this library, as it requires a new fix at a regular interval. In the example code you will find a function called smartDelay()copy this function in to your code and use this instead. It looks like this.
static void smartDelay(unsigned long ms)
{
unsigned long start = millis();
do
{
while (ss.available())
gps.encode(ss.read());
} while (millis() - start < ms);
}
Put this code in the bottom of your code at call smartDelay(5000); instead of delay(5000); in the bottom of the void loop()
You should also place the call to displayInfo(); just below smartDelay() like this.
void loop()
{
while (ss.available() > 0)
if (gps.encode(ss.read()))
if (millis() > 5000 && gps.charsProcessed() < 10) {
Serial.println(F("No GPS detected: check wiring."));
while(true);
}
smartDelay(5000);
displayInfo();
}
Edit: A even better way
A even better way, especially if you like to do other stuff while you showing the data, is to use millis(). this will also be called more precise every 5 sec.
You wil have to declare a variable at the top for this to work. It will look like this.
long timeToDisplay = 0; // Declare this at the top
void loop()
{
while (ss.available() > 0)
if (gps.encode(ss.read()))
if (millis() > 5000 && gps.charsProcessed() < 10) {
Serial.println(F("No GPS detected: check wiring."));
while(true);
}
if(timeToDisplay <= millis()) {
timeToDisplay = millis() + 5000;
displayInfo();
}
}
slash-dev give me an answer on how to get data in 5 secs interval from GPS.He also provided some improved methods, which when i followed obtains perfect output.It solved some bugs in the code i provided too.Link to the answer :
https://stackoverflow.com/a/43009260/7699452
result :
https://i.stack.imgur.com/pzvsu.png
I have a sensor connected to the arduino uno pin 5 and it reads the sensor value every second and send out a sms every 1 minute. This works.
void loop() {
// read_sensor_and_store(5);
// increment_time_counter();
// if_time_counter is 60000 miliseconds then send the sms
delay(1000);
}
While this works, I want to call another function, say, read_another_sensor(pin, delay_0, count). What this will do is read the particular pin 'count' number of times with delay of 'delay_0'. (baically it will run a for loop with the given delay).
Now if I have something like this
void loop() {
// read_sensor_and_store(5);
// read_another_sensor(7, 2000, 4);
// increment_time_counter();
// if_time_counter is 60000 miliseconds then send the sms
delay(1000);
}
This too will work but while executing the read_another_sensor() the time will elapse and I will miss few readings of pin 5. Is there a way to execute these two functions in parallel or any other way to achieve the objective of "calling of read_another_sensor will not affect the continuous periodic functionality of read_sensor_and_store"
Appreciate any insight on this matter.
Thank you
Extending the answer of the user above, you can do something like this to allow you to spawn sensor2 task with arbitrary (multiply of 50 mili) periods:
const int ONE_SECOND = 1000;
const int ONE_MINUTE = 60*ONE_SECOND;
// 50 mili - has to be upper bound of total execution time
// has to be [GCD][1] of periods of all your tasks
const int BASIC_PERIOD = 50;
// FRAMES*BASIC_PERIOD has to be [LCM][2] of periods of all your tasks
const int FRAMES = ONE_MINUTE / BASIC_PERIOD;
void read_another_sensor(delay_0, count) {
g_delay = delay_0;
g_count = count;
}
...
void loop() {
if ((unsigned long)(millis() - previousMillis) >= BASIC_PERIOD) {
previousMillis = millis();
frame_counter = (frame_counter + 1) % FRAMES;
if (frame_counter == 0)
send_sms();
if frame_counter % ((FRAMES * BASIC_PERIOD) / ONE_SECOND) == 0
read_sensor();
if frame_counter % ((FRAMES * BASIC_PERIOD) / g_delay) == 0
if (count > 0) {
count--;
read_sensor_2():
}
}
}
}
If you need to use more periodic tasks with periods that are not multiply of 50 mili, your system becomes complex wor have high rate of deadline misses, please read about "cyclic executives".
GCD
LCM
In general you can replace delay(1000) with a timestamp comparing code to reach your goal. The code construction (see below) is often used if one function needs to executed in an different interval than others or if you want to combine sketches. There are already several sources on the web and stackoverflow (e.g. see arduino-combine-sketches).
unsigned long interval=1000; // the time we need to wait
unsigned long previousMillis=0; // millis() returns an unsigned long.
void setup() {
//...
}
void loop() {
if ((unsigned long)(millis() - previousMillis) >= interval) {
previousMillis = millis();
// ...
}
}
//...
So in your case you need something like the following code, that I have not tested, but hopefully gives you an idea how you can manage the functions without delay():
unsigned long interval=1000; // the time we need to wait (read_another_sensor)
unsigned long previousMillis=0; // millis() returns an unsigned long.
void setup() {
//...
}
void loop() {
// every 1000 millisecs (=delay(1000))...
if ((unsigned long)(millis() - previousMillis) >= interval) {
previousMillis = millis();
read_another_sensor();
//...
}
read_sensor_and_store() // continuous periodic
// ...
}