Send it once until

Hi,

I am still a newbie to coding for Particle. I created a script to send out an alert when an input sensor to A0 is higher then 0, the problem is that I keep getting the alert multiple times until the sensor connected to A0 is 0.

Anybody a hint how I should write the code so it will send me the alert once. It may send it a second, and third time etc, but only when it first reach 0.
Thanks in advance.

My current code:

static int button = A0;

void setup() {
    pinMode(button, INPUT);
}

void loop() {
    int alarmstatus = analogRead(button);
    if (alarmstatus == 0 ) {
        // button pressed
        Particle.publish("pushbullet", "This is the alert text to pushbullet", 60, PRIVATE);
    }
   
}

There’s a couple of things going on;
If you’re trying to read a button, or any “on/off - High/Low” sensor, you’re talking about binary states, in which you’d use a digitalread.
You’ve got to make sure your pins aren’t ‘floating’, since that could give false readings. Pull-ups/downs help, depending on your setup.
Seeing as you’re not using any delays, you’ll blow through the publish limit near instantly.

The code you posted should do the opposite of what you say it's doing; it should only publish when the reading is equal to zero, not when it's above zero. It would be helpful to know what kind of sensor you're using. Relying on an analog sensor to read all the way down to zero is probably asking for trouble. Is your sensor actually analog?

@Moors7 thanks for your responses. The sensor is an alarm system which have an output. I am not sure about the type of output, but when I did connect it to a digital read it was not giving any difference when there was an alarm in Tinker. Then when I used the analog read I got an output of 0 in Tinker when there is no alarm and a higher number in Tinker (don’ t know exactly, because it changes a little) when there is an alarm. When I turn of the alarm, the output will go back to 0. That is the reason why I used the analog read function. I used it for months now, and it is constantly 0. The only problem I have is that it gives multiple messages instead of 1 with the code I used. When I used a delay I even get more messages till the alarm goes off. So the question still is, how to get 1 message instead of multiple.

@Ric Thanks for your response too. I don’ t understand why I should send a message when the state is zero, because when it is zero there is no alarm, and then I don’ t want to have a message. Maybe you could explain more why to do this, I could misunderstand. Please see the response before to Moors7 about the sensor.

I believe you want to send the message when the alarm state changes...

something like this:

const int button = A0;
int lastStatus = 1;

void setup() {
  pinMode(button, INPUT);
}

void loop() {
  int alarmStatus = analogRead(button);
  if (alarmStatus == 0 and lastStatus != 0) {
    // button pressed
    Particle.publish("pushbullet", "This is the alert text to pushbullet", 60, PRIVATE);
  }
  lastStatus = alarmStatus;
}

but it is odd to use analogRead() on a simple switch.

1 Like

The point @Ric was getting at is that your original code uses alarmStatus == 0 to trigger the alarm but your problem description states you want the alarm triggered when the read value is > 0 - see the contradiction?
Hence @BulldogLowell's code is correct in essence but won't do what you want since he just took your code and corrected it to properly do what your original code intended - but that wasn't what you wanted to start with :wink:

BTW, I'd probably go for a higher threshold and to figure out what a safe threshold would be take the raw reading of some exemplary alarm triggers, reduce it by 10-20% and use that for a alarmStatus > X.

Also for analogRead() you don't need to set the pinMode()
https://docs.particle.io/reference/firmware/photon/#analogread-adc-

pre 0.5.3 the behaviour was "worse" so with that you should not set pinMode()

I wasn't suggesting that you should sent the message when the state is zero, I was pointing out that that is what the code you posted does (as @ScruffR also mentioned in his response).

Wow! All I can say is you were lucky. You could have destroyed your device by connecting it to something with an unknown output. 5 volt and 12 volt outputs are quite common, either of those would have fried your analog input. Do you not have a data sheet for the alarm system that states what the output is? If not, or you can't find one online, then you should check the output with a voltmeter to see what it is. What kind of analog readings are you getting now when there is an alarm?

It would also be useful for us to see how you have wired your device to the alarm system.

3 Likes

Whoops, my bad. I made a mistake in the explanation , and some research how I connected it to the alarm system .

The alarm system is an omni 624 , the data sheet is at http://www.safelifesecurity.com/manuals/omni_624.pdf . There is a trigger/relay port on the alarm system, so when there is an alarm the circuit will be closed. The circuit on the trigger/relay port is connected to A0 and GND. By default the A0 port has an high value (I meassured +/- 2100 as value with tinker on the port), when the circuit is closed (A0 connected to GND) the value will drop to 0 , and then I need a message to pushbullet, but only once and not multiple times.

I used the code of @BulldogLowell (btw, thanks a lot!), but that didn’t work, still received multiple messages. Anyhow, it gives me inspiration to build the following:

const int button = A0;
int lastStatus = 1;

void setup() {
  pinMode(button, INPUT);
}

void loop() {
  int alarmStatus = analogRead(button);
  if (alarmStatus == 0 and lastStatus != 0) {
    // button pressed
    Particle.publish("pushbullet", "This is the alert text to pushbullet", 60, PRIVATE);
    lastStatus = 0;
  }
  if (alarmStatus != 0 and lastStatus == 0) {
    lastStatus = 1;
  }
}

This one is almost working. When the circuit is closed for the first time (A0 == 0) for the first time, I got only 1 pushbullet message. So that looks great. Then when the circuit is open, and closed for a second, third etc time, I got multiple messages again. I don’t understand why it is sending 1 message for the first trigger, but not working for all triggers after by sending multiple messages.

HI @jurjendevries

You should not use analogRead for a digital (on/off) input in my opinion. It is just bad practice.

Any input on a Particle device that is floating, that is not constantly electrically connected to some source or switch with a pull-up or pull-down, will generate random values. So for your relay output from the security panel with a NO contact to ground, you need a pull up resistor. Either a real physical pull up resistor or setting that input to have pull up in your code.

Anything else will be unreliable.

Reading the install/set-up sheet for the alarm, it says: “For UL installations, the
trigger outputs shall be connected to devices rated to operate over the range from 10.1-14.0VDC at 50mA” so I hope you have a relay on that since 12VDC nominal output voltage will fry your Particle device’s input.

1 Like

Hi @bko
Thanks. I understand it create random values, but why is it bad as most of you mention? As long as the random value is not 0? It works well for weeks, except that I get multiple messages with the loop, but as far as I understand it is not because of the analog port.

The relay it self didn’t gave 12VDC to the port to the particle circuit, is is only closing the particle circuit between the A0 and GND port on the particle, which makes the value 0.

Can I do that with a digital pin to? Then do I need to connect D0 and 3v3 for example as a closing circuit by the relay? And I guess it need to be connected to ground too, or not?
Then I guess I still need to change something in the code to not get the messages multiple times?

It could be bad, because it could be 0. There is no reason to use analog to read a switch closure - you have much more range between what is considered on and what is off when using digitalRead. The way you're doing it, even a small bit of noise could result in a reading of 1 or 2, which would look like a trigger when it's not.

I read the same thing @bko did in the data sheet, and I'm not sure what it means. There was something about connecting to VBELL to get a positive reference voltage, so maybe without that, there is no voltage, only a contact closure. However, I wouldn't connect anything to it without actually measuring the output with a volt meter. If it is only a contact closure (no voltage either active or inactive), then you could connect Relay1 or Relay2 to A0 (or any other pin - A0 can be used as a digital pin also), and connect the ground to the Photon's ground. Set the input to INPUT_PULLUP, and use digitalRead to read the value. Using INPUT_PULLUP will cause the pin to read HIGH when the relay is open and LOW when it is closed.

1 Like

Hi @jurjendevries

Maybe the key to unlocking this is the fact that all of the analog pins can also function as digital pins. The analog features are added on, so you can digitalRead(A0) just the same as D0.

The reason you are getting multiple messages may be due to contact bounce. When you close a mechanical switch or relay, the contacts do not slam shut and stay shut–they bounce around making and unmaking contact many times quickly before setting into the connected state.

You can debounce switches and relays in software or in hardware but not doing it could explain what you are seeing.

And most of these issues would have been dealt with when adhering to the suggestions provided in the first reply you got

2 Likes

Hi @bko , thanks again for your explanations. How can I debounce in the software?

Because a lot of feedback is moving from analogread to digitalread, I changed ports and code. Currently using D0 and closing with 3V3 to get a HIGH value. Currently testing without the alarm system by making a closing by hand with a jumper cable on a breadboard.

However I still got multiple messages when the circuit is closing, so maybe your hint in debounce will help, or @ScruffR / @Moors7 hint to Pull-ups/downs. But I don’ t know which to use in this setup.

The current code:

const int button = D0;
int lastStatus = 1;

void setup() {
  pinMode(button, INPUT);
}

void loop() {
  int alarmStatus = digitalRead(button);
  if (alarmStatus == HIGH and lastStatus == 1) {
    // button pressed
    Particle.publish("pushbullet", "This is the alert text to pushbullet", 60, PRIVATE);
    lastStatus = 0;
  }
  if (alarmStatus == LOW and lastStatus == 0) {
    lastStatus = 1;
  }
}

Hi @jurjendevries

The basic idea with debouncing is to read the input at least twice some short amount of time apart, making sure you get the same value both times. There libraries that will help you do that but I will try to show you some code below.

If you contact closure (switch or relay) is from input to 3.3V, then you need a pull down on the input to hold it low until you close the switch. Without that you can get random values.

You also need to worry about the Particle.Publish() limits of averaging one per second but I’m going to ignore that for now.

const int button = D0;
int lastStatus = 1;

void setup() {
  pinMode(button, INPUT_PULLDOWN);
}

void loop() {
  int alarmStatus1 = digitalRead(button);
  delay(5); // wait 5ms
  int alarmStatus2 = digitalRead(button);
  if (alarmStatus1 == HIGH && alarmStatus2 == HIGH && lastStatus == 1) {
    // button pressed
    Particle.publish("pushbullet", "This is the alert text to pushbullet", 60, PRIVATE);
    lastStatus = 0;
  }
  if (alarmStatus1 == LOW && alarmStatus2 == LOW && lastStatus == 0) {
    lastStatus = 1;
  }
}

2 Likes

Moving from A0 to D0 was not required as even Ax pins also can be used as digital I/O.

There is no question about debounce or pull-resistors. You need both.
As @bko already said:
The pull-resistor is to provide a stable level when the pin is unconnected (aka floating) while the debounce is needed to prevent any "ghost" triggers by the physical switch bounces (opens/closes in rapid succession due to bouncing contatcs).

1 Like

Thanks so much. It is working with the new code. Only thing I have added extra is another delay after:

int alarmStatus2 = digitalRead(button);

I added this because else I think in a loop it is doing alarmStatus1 directly after alarmStatus2.

I have to find out how a pull-resistor is working, what kind of pull-resistor value I need and how to add it to the circuit. I tried to search at google, but not clear yet. Any hints are welcome @ScruffR

If any of you is in Utrecht, The Netherlands , let me know. We eat cake :slight_smile:

1 Like

This takes care of the pull-down resistor so no external part is required.

2 Likes

The reasoning behind pull-resistors is this:
When you leave a hi-Z input pin unconnected it will virtually pick up any "stray" charge in the atmosphere and hence report random readings. These random readings may cause multiple transitions between hight and low thresholds producing unexpected triggers.

And for that reason you add a high value resistance to "drain off" these "stray" charges providing a definite HIGH or LOW level for the case the pin is unconnected (e.g while a connected switch is not closed).

In order to detect when the switch closes, you need your default level to be opposite to the level your contact is closing to. So for a switch closing to GND you need a pull-up resistor and for a switch closing to Vcc you need a pull-down resistor.

As @bko already mentioned, you can have the ”C apply internal pull-resistors (INPUT_PULLUP or INPUT_PULLDOWN) which are in the region of 40k. In some cases (e.g. long wires, lots of EM noise, ...) these weak pull-resistors might not suffice, then you may go for INPUT and attach your own stronger pull-resistors 10k ~ 2k2 are common.

We can send our fellow Elite @Moors7 round, to relieve you of some :cake: in our behalf :wink:

3 Likes