1

I am monitoring an external device that outputs a boolean value of False when the condition is not met, and a boolean value of True when the condition is met. The problem is, the boolean value is output more than once after the condition is met.

This is what the output looks like:

False
False
False
False
True
True
False
False
False

So what I would like to be able to do is monitor this value and execute a simple function, only once for each time the boolean changes from False to True. I have seen other languages that have a simple "on change" function, so I know its possible and probably a lot easier than I'm making it out to be.

Currently I have this. For the sake of explaining this, the variable "ext_data" is used as the external data that the python script is monitoring.

while True:
    if ext_data == True:
        print("true")
        pass

This prints "true" every time the boolean is equal to true, and when it resets itself the boolean is still set to True, so I get multiple readings when I only want one.

Thanks to anyone who helps out!

edit:

Updated code using the EdgeDetector class written by holdenweb.

ext_data = True/False  # This returns True or False depending on the state
                       # of the variable. It is constantly monitored and
                       # could change at any moment, which is why I'm 
                       # running the function in a while True loop
def printer():
    print(u'something%s' % serialport.cts)
    post_to_api('cycle_count')

def myfunction():
    return ext_data

test_subject = EdgeDetector(myfunction, printer)

while True:

    test_subject.test()

This still returns occasional duplicate posts. It was mentioned that this data needs to be in an array for the EdgeDetector Class to work properly, how would you suggest putting this data into an array without creating an infinitely long array?

1
  • As far as I know, the only person who mentioned storing the data in an array (list, in fact) was you. When you say it "returns duplicate posts," how are you determining that? The fact of their matter is that, unless there's a bug (always possible) it's impossible for the code to detect an edge unless there's been a high-to-low transition first (since technically I should really have called it RisingEdgeDetector, but this assumes knowledge of the polarity of the signal). Since you are polling, it's possible that you will miss short-lived pulses. Commented Apr 10, 2017 at 5:58

3 Answers 3

3

The way your question is framed doesn't make it clear how the external value is acquired. This must involve storing state information - specifically, the last value that was read.

While it would be possible to store this in a global variable, this is not good practice - while it may satisfy your immediate needs, if you try to create a re-usable software component based on it, it will break in the event that some other code in the same module decides to use the same global variable for other purposes. So a class might be more satisfactory.

The following class is written to receive a function that, when called, returns the value of the external variable. This is passed to the class's __init__ as parameter read_signal. The second parameter, action, is a function that the object should call when it detects a False to True transition.

Casting it in this form means you can easily integrate it inot any program, and should you fund yourself having to deal with many external variables you can just create an EdgeDetector for each one.

class EdgeDetector:
    """Detects False to True transitions on an external signal."""

    def __init__(self, reader, action):
        self.reader = reader
        self.action = action
        self.last_value = reader()    # initialise value

    def test(self):
        new_value = self.reader()
        if new_value and not self.last_value:
            self.action()
        self.last_value = new_value

if __name__ == '__main__':                 # simple self-test
    vlist = [False, False, False, True, True, False, True, True, False, False, False, True]
    vgen = (v for v in vlist)              # generator for value sequence

    def test_reader():                     # generate test sequence
        value = next(vgen)
        print("Read:", value)
        return value

    def printer():
        print("Edge transition detected")

    test_subject = EdgeDetector(test_reader, printer)
    for i in range(len(vlist)-1):
        test_subject.test()

Normally you would import this module into your program, making it easier to re-use and keeping the implementation details well away from your main code. When run as a program the self-test code shows you the sequence of input values and when the transitions are detected, allowing you to have a little more confidence in your code before deploying it. The output of the self-test is

Read: False
Read: False
Read: False
Read: True
Edge transition detected
Read: True
Read: False
Read: True
Edge transition detected
Read: True
Read: False
Read: False
Read: False
Read: True
Edge transition detected
Sign up to request clarification or add additional context in comments.

6 Comments

This is exactly what I was looking for, every search for edge detection returned results about canny edge detection for image processing. Thanks for such a detailed response
Glad to be of assistance
Quick question, how would you do this when monitoring a live variable without storing the data of the variable? I tried saving the last two values of the live variable into a list, and then rewriting the list each time the list reaches two values. I.e. the list would be [False, False] and then it would keep getting rewritten and I would test for when it is equal to [False, True], however I still get double readings from that.
Maybe you were emptying it after it got to length 2? The correct way to add a new value into your list would be something like list = [list[-1], new_value] but you'll have to initialise it to have at least one value in it to avoid throwing an exception accessing element -1.
sorry to bug you again, I edited my original post to better clarify how i am obtaining the data. How would you suggest using your Class with a constantly changing variable like that? To be more clear, this variable returns the state of two wires. If the wires are not shorted together, it returns false. If they are connected, it returns true. I am running this in a while true loop to constantly monitor these wires, as I need to know every time they are shorted together.
|
1

You could have an internal variable which you keep track off (pseudo code)

executed = false

if(!executed && output_from_device == true)
    print("true")
    executed = true

But I don't know your entire setup, so it may be better to create some form of event handling for it rather than a naive approach.

3 Comments

executed should then be updated to false once your output_from_devicechanges from true to false like so : if executed and not output_from_device: executed = false
I've tried keeping an internal variable like suggested but wasn't able to get it to work. I'll give it another go. So by updated "executed" to false, this allows the loop to keep going until the next instance, correct?
@Dennis is this how the code should be written? Because I'm still getting multiple readings. var is the boolean data, i put the '\n' to indeicate line breaks while True:\n if not executed and var:\n print("executed")\n executed = True\n if executed and not var:\n executed = False\n
1

Why not somehting simple as :

#Input Boolean
inputData = [True,False,False,False,True,True,False]


foo =''

for i in inputData:
   if foo != i:
      print("Yo")
      foo = i

First element of InputData will trigger print because foo was empty, then as foo = True, the next time the print will be triggered when inputData element will be False ... etc ...

2 Comments

Because this is monitoring live data so I don't think you could run a for loop, as the upper limit would be infinite right? This needs to monitor the data and print("yo") in real time instead of logging the data and then filtering through the data
So it's kind of 'while True' loop where at the beginning of the loop you receive input from something else, each time you receive this data you can check with the 'if' condition I wrote no ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.