1

At the first part of the code(Initializing relays) the GPIO output works as expected, but at the second part it doesn't works, I have break my head for the last four hours trying to find out what is happening here...

The updateGPIOstatuses method is executed as a thread:

_thread.start_new_thread(updateGPIOstatuses,(gpioCheckerInterval,pins,))

The actual behaviour is: The second part of this code doesn't turn on the relay from the relay board... But at the first part it does the job.

Any idea?

def updateGPIOstatuses(gpioCheckerInterval,gpioConfig): 
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    gpioConfig=[19]

    #First part
    print('Initializing relays')

    for i in gpioConfig:   
        print('Relay '+str(i)+' on..')     

        GPIO.setup(i, GPIO.OUT)
        GPIO.output(i, GPIO.HIGH) 
        time.sleep(0.5)
        GPIO.output(i, GPIO.LOW)  
        time.sleep(0.5)   
        print('Relay '+str(i)+' off..') 

    print('Initialization done')

    #Second part
    while True: 
        #statuses=dbQuery('2','')
        statuses={'gpio8': 0, 'gpio7': 0, 'gpio10': 0, 'gpio4': 0, 'gpio2': 0, 'gpio1': 1, 'device': '1', 'gpio5': 0, 'gpio3': 0, 'gpio9': 0, 'gpio6': 0}

        time.sleep(0.5)

        after=''
        print(' ')
        j=1

        for i in gpioConfig: 
            newState=statuses['gpio'+str(j)]
            j+=1

            if newState==1:
                print(1)
                GPIO.output(i, GPIO.HIGH)
                time.sleep(0.5)

            else:
                print(0)
                GPIO.output(i, GPIO.LOW)
                time.sleep(0.5)

        time.sleep(gpioCheckerInterval)  

EXTRA: I forget to mention, the relay phisical state changes in the first part, but not in the second one, if I check the pin state at the second part after changing the pin value I get the correct value, but it doesn't correspond to the phisical state of the current relay.

In resume, seems like the pin is correctly settled but the relay doesn't, and as I said, only in the second part.

12
  • 1
    Your code is really confusing. You are passing in gpioConfig then setting it to [19] is that your intention?
    – CoderMike
    Jan 8, 2020 at 23:40
  • 1
    Does HIGH or LOW turn your relay on?
    – CoderMike
    Jan 9, 2020 at 0:02
  • 1
    setting it to 19 just for this example, normally it is passed from outside, I just want to test with one pin Jan 9, 2020 at 0:36
  • 1
    Yes, HIGH or LOW toggles the relay in the first part but not in the second one Jan 9, 2020 at 0:36
  • 1
    I added an extra info Jan 9, 2020 at 0:40

2 Answers 2

1

In the second section of code:

gpioConfig=[19] so i=19

j=1 and never increments

newState=statuses['gpio1'] which gives 1

As newState == 1, GPIO 19 is only every set to HIGH

6
  • I read the OP's code a third time, and have the following quick and dirty conclusion. (1) The code is perhaps bug free, though seemingly written by another python newbie in a hurry, therefore poorly documented.
    – tlfong01
    Jan 13, 2020 at 4:52
  • (2) The OP seems not understanding the overall working of the code (one thread perhaps using senors, then updating a database, where the OP's thread read the database for a dictionary (yes, not list), to update the stated of GPIO pins, which in turn control the relays.
    – tlfong01
    Jan 13, 2020 at 4:53
  • (3) I guess the OP is trying to use test data samples to help understand the code. But unluckly he uses the following wrong data (a) [19] as the testing pins list (I think a better choice is something like [4, 2, 19, ], which would then trys the elements in the dictionary, (not necessary in positional order, because it is a dict, not a list) . Anyway, the variable "j" would then be incremented, would not have misled @CodeMike that it was a bug.
    – tlfong01
    Jan 13, 2020 at 5:00
  • (4) The OP made another bad choice in uncommenting this statement: "#statuses=dbQuery('2','')" and used a perhaps unnecessarily long dict sample: statuses={'gpio8': 0, 'gpio7': 0, ... } which unluckily does not include 'gp1o19' which the OP picked earlier, ... In short, there are three bad debug choices (a) bad list [19], with gpio19 no chance to try out it luck, (b) not starting another thread which might update database, so the gpio19 might come up sooner or later to help debugging. (c) the fixed sample dict does not inlude gpio19 to try it luck. ,..
    – tlfong01
    Jan 13, 2020 at 5:04
  • If only two of the above bad debugging sample/trick are used. the OP might have a better chance of seeing the big picture more clearly. (5) Actually there are other minor things that make the code confusing (a) using the numericbale variable to convert a string value 'gpio'+str(j), eg convert integer 1 to string 'gpio1. This conversion do confuses newbies who don't know dictionary, why this is necessary.
    – tlfong01
    Jan 13, 2020 at 5:10
0

Update 2020jan12hkt2102

I read the program a second time and found the following things:

(1) This statement:

_thread.start_new_thread(updateGPIOstatuses, pins,)) 

seems to call function "updateGPIOstatuses()" with two arguments "gpioCheckerInterval" and "pins". I guess "pins" means a list of GPIO pin numbers, eg 19, 8, 3, etc.

But in the function definition:

"def updateGPIOstatuses(gpioCheckerInterval, gpioConfig)"

The second argument "gpioConfig" should positional correspond to "pins". So it is misleading to use two different names for the same argument.

(2) In definition body, the second argument "gpioConfig" which should be passes as a list of pins, but assigned to 1 which does not make sense. Yes, the OP, says that the assignment is just "an example" (for debugging purpose, I think.).

(3) Since the example is only a single element list, so as @CodeMike points on, the loop variable "j" is not incremented. But I think this is actually not the bug.

I think I understand why Part 1 is OK, but not Part 2. The reason is the following: gpioConfig is [19}, so "Gpio 19 Relay" switches on and on as expected.

But the status dictionary {'gpio8': 0, 'gpio7': ...} does NOT including any element "gpio19", therefore "GPIO 19 Relay" is not changing its state. In other words, if the dictionary includes ‘gpio19:x', then Part 2 should update the state of "GPIO 19 Relay".

I know it is confusing, even if one know python list and dictionary. I think I can later, modify one of my similar GPIO modules to explain. But it might take me a long while to do this explanation job.

Question

User Requirements/Functional Specification

v0.1 - How to debug my program is less than 4 hours?

v0.2 - How to use Rpi control multiple relays?

v0.3 - How to use Rpi4B Thonny python 3.7.3 to control high logical level triggered relays?

v0.4 - How to use Rpi4B Thonny python 3.7.3 to control GPIO pins which in turn turn on and off multiple high level triggered relays?

v0.5 - How to use Rpi4B Thonny python 3.7.3, Rpi.GPIO.0.7.0/GpioZero 1.5.1 to control, interruptable/event driver/multiple processing/threading Rpi GPIO pins or MCP23017/s17 extended GPIO pins which in turn control multiple, up to 64 high level triggered 3V3/5V0 relays?

v0.6.1 - How to use GPIO/GpioZero to control gpio pin pins? v0.6.2 - How to use gpio pins to control multiple relays? v0.6.3 - How to use python list/dictionary data structure/algorithm to develop declarative style relay control systems.

Note 1 - V0.6.3 above is what the OP actually using python threading, dictionary declaration, list processing techniques, to write his program. Now I am going to use his program as a case study to explain to python newbies, how his program works, and how to guarantee successful debugging in less than 40 minutes.


Answer

Ah, let me see, I think I can very likely find the bug in less than 40 minutes,

(1) Get my Occam's razor (KISS),

(2) Morning coffee time, ..

Update - Since @CodeMike has already given an answer acceptable to the OP, I will, nevertheless, still complete my answer, focusing on debugging skills (actually software engineering/development methodology), to encourage/comfort those newbie programmers like me, without sharp eyes and a clear/vivid mind, can still catch the nasty bug sooner or later, with guarantee that no one's head will be broken.


GPIO Controlling Relay - Programming and Debugging Notes

1. Make it as simple as possible (Occam Razor Cutting) steps

Step 1 - Cutting too higher level stuff/functions, eg multi-threading

The multi-threading function to start a new thread (update GPIO pin(s) status), in not relevant to debugging at the current, lower, thread level, so should be cut first.

thread.start_new_thread (updateGPIOstatus, (updateInterval, gpioPinList,))

Reference - Python - Multithreaded Programming - tutoriaspoint

The general function definition is the following:

thread.start_new_thread (function, args[, kwargs])


Step 2 - Cutting to lower level stuff/functions (eg. GPIO initialization)

The OP uses the following statement often, and to make things not go wrong easily, it is a good idea to make it a "function", sort of cutting lower level stuff and hide it (Info Hiding) or abstract it (ADT, Abstract Data Type).

GPIO.setup(i, GPIO.OUT)
GPIO.output(i, GPIO.HIGH) 

The other GPIO initialization steps, though used only once, can also be hidden in a higher level function. Depend on application, there are different ways to do the multi-level, nested function definitions. For our case study here, let me give an example program with these functions. My program for now is called gpioControl001.py, later I will up grade, step wise refine/refactor to to ledControl001.py, relayControl001, relayMultiThreadControl001, and so on.

Update 2020jan09hkt1727

Now I have written a very simple program to show the python newbies the idea of using functions to hide/abstract the complicated things inside the function, to make a program simple. The sample program listing is in Appendix E. The main function is to toggle BCM numbering GPIO pin 21 times. I recommend newbies to skim the sample program, then rewrite their own GPIO toggle, test it, and compare and contrast with the sample program which is of course not perfect, but just to show the idea of structured programming and functional programming. I also recommend newbie to use simple print statements for debugging, as shown in the sample program. It is also a good idea to include a sample output, so that other newbies can verify that your program is actually bug free.

2. List processing and TDD (Test Driven Development)

Now let me do a selfie walk through. What I have been doing so far is something in the old days the programming technique "Top down design, bottom up implementation", or nowadays called "Agile Style Incremental/Continuous Test/Integration" system integration. You may like to read the TDD listing processing discussion in Appendix F - TDD Discussion:Software Enggr Stack Exchange Forum below. Warning to newbies who have never heard of TDD: It might take you much longer than an hour to read, goggle, and digest.

I have already shown your how to use tested function (with sample output so newbies can verify) along with my writing of the very basic GPIO functions. What I am doing next is showing the very powerful programming tool, called list processing, the idea started in the 1950's at MIT as LISP programming language, later Scheme, ... then Harvard Haskell, then ... Scala (using list to do "scaling up") which according to a very recent Stack Overflow survey, ranks top of the most highly paid developer's programming language.

Coming back to the OP's "debug for me" question, I found him using a couple of statements to toggle a GPIO pin, which I guess is a form of TDD. I have already written a GPIO toggle function, to make his testing code simpler. The OP also uses python dictionary and list processing techniques. Dictionaries might have confused newbies in the beginning, but makes the program more expressive, self documenting and easier to maintain/expand and debug. I will show how to do dictionary later. For now, I will start list processing, which actually is also a form of abstraction, ...

/ to continue, ...

References

(1) Python map() Function - Programiz.com 2/5

(2) Python map() Function - W3Schools.com 2/5

(3) Python map() function - TutorialsTeacher.com 3/5

(4) Python map() function - GeeksForGeeks.org 4/5

(5) Python map() function - JournalDev.com 5/5

(6) Python map(), filter(), and reduce() Functions (No looping, no branching) - LearnPython.org

(7) Python map() function And List Comprehension Forum Discussion - StackOverflow.com

(8) Python map(), filter() and List Comprehension Forum Discussion - StackOverflow.com

(9) Scala (programming language) -Wikipedia

(10) Occam (programming language) - Wikipedia

(11) Lua (programming language) - Wikipedia


Appendices

Appendix A - The OP's Original Buggy Code

relay code**strong text**


Appendix B - KISS

KISS Principle - Wikipedia

The principle most likely finds its origins in similar minimalist concepts, such as Occam's razor, Leonardo da Vinci's "Simplicity is the ultimate sophistication", Shakespeare's "Brevity is the soul of wit", Mies Van Der Rohe's "Less is more", Bjarne Stroustrup's "Make Simple Tasks Simple!", or Antoine de Saint Exupéry's "It seems that perfection is reached not when there is nothing left to add, but when there is nothing left to take away". Colin Chapman, the founder of Lotus Cars, urged his designers to "Simplify, then add lightness". Heath Robinson machines and Rube Goldberg's machines, intentionally overly-complex solutions to simple tasks or problems, are humorous examples of "non-KISS" solutions.

A variant – "Make everything as simple as possible, but not simpler" – is attributed to Albert Einstein, although this may be an editor's paraphrase of a lecture he gave.


Appendix C - Occam's Razor

Occam's razor - Wikipedia

Occam's razor is the problem-solving principle that states that "Entities should not be multiplied without necessity."

The idea is attributed to English Franciscan friar William of Ockham (1287–1347), a scholastic philosopher and theologian who used a preference for simplicity to defend the idea of divine miracles.

It is sometimes paraphrased by a statement like "the simplest solution is most likely the right one", but is the same as the Razor only if results match.

Occam's razor says that when presented with competing hypotheses that make the same predictions, one should select the solution with the fewest assumptions, and it is not meant to be a way of choosing between hypotheses that make different predictions.

Similarly, in science, Occam's razor is used as an abductive heuristic in the development of theoretical models rather than as a rigorous arbiter between candidate models.

In the scientific method, Occam's razor is not considered an irrefutable principle of logic or a scientific result; the preference for simplicity in the scientific method is based on the falsifiability criterion.

For each accepted explanation of a phenomenon, there may be an extremely large, perhaps even incomprehensible, number of possible and more complex alternatives.

Since one can always burden failing explanations with ad hoc hypotheses to prevent them from being falsified, simpler theories are preferable to more complex ones because they are more testable.


Appendix D - William Occam

William of Ockham (Occam, c. 1280—c. 1349) - IEP

w occam

In logic, Ockham presents a version of supposition theory to support his commitment to mental language. Supposition theory had various purposes in medieval logic, one of which was to explain how words bear meaning.

Theologically, Ockham is a fideist, maintaining that belief in God is a matter of faith rather than knowledge. Against the mainstream, he insists that theology is not a science and rejects all the alleged proofs of the existence of God. Ockham’s ethics is a divine command theory. In the Euthyphro dialogue, Plato (437-347 B.C.E.) poses the following question: Is something good because God wills it or does God will something because it is good? Although most philosophers affirm the latter, divine command theorists affirm the former.

Ockham’s divine command theory can be seen as a consequence of his metaphysical libertarianism. In political theory, Ockham advances the notion of rights, separation of church and state, and freedom of speech.


Appendix E - Sample Program 1 - Toggling GPIO BCM Pin 21

# fgpio243.py  tlfong01  2020jan09hkt1713

import RPi.GPIO as GPIO
from time import sleep

# *** Select GPIO pin numbering scheme *** 

def setGpioPinNumberingSchemeBCM():
    print('Set GPIO Pin Numbering to BCM')
    GPIO.setwarnings(False) 
    GPIO.setmode(GPIO.BCM)
    return

# *** Setup GPIO pin to output mode and init to low or high

def setupGpioPinOutputMode(gpioPin):
    GPIO.setup(gpioPin, GPIO.OUT)
    return

def setGpioPinHigh(gpioPin):
    GPIO.output(gpioPin, GPIO.HIGH)
    return

def setGpioPinLow(gpioPin):
    GPIO.output(gpioPin, GPIO.LOW)
    return

def setupGpioPinOutputModeInitLow(gpioPin):
    setupGpioPinOutputMode(gpioPin)
    GPIO.output(gpioPin, GPIO.LOW)
    return

# *** Toggle GPIO pin ***

def toggleGpioPin(gpioPin, highSeconds, lowSeconds, totalCount):
    print('testToggleGpioPin(), ...')
    setupGpioPinOutputModeInitLow(gpioPin)  
    for count in range(totalCount):
        print('Count =', count)
        print('  GPIO Pin', gpioPin, 'Now High')
        setGpioPinHigh(gpioPin)
        sleep(highSeconds)
        print('  GPIO Pin', gpioPin, 'Now Low')
        setGpioPinLow(gpioPin)
        sleep(lowSeconds)
    print('End of program.')
    return

# *** Main ***

setGpioPinNumberingSchemeBCM()
setupGpioPinOutputModeInitLow(gpioPin = 21) # BCM GPIO Pin 21 = 40 pin header Pin 40
toggleGpioPin(gpioPin = 21, highSeconds = 1, lowSeconds = 1, totalCount = 3)

# *** Sample output ***

'''
>>> %Run fgpio243.py
Set GPIO Pin Numbering to BCM
testToggleGpioPin(), ...
Count = 0
  GPIO Pin 21 Now High
  GPIO Pin 21 Now Low
Count = 1
  GPIO Pin 21 Now High
  GPIO Pin 21 Now Low
Count = 2
  GPIO Pin 21 Now High
  GPIO Pin 21 Now Low
End of program.
>>>
'''

# *** End of Program ***

Appendix F - List processing and TDD

References

(1) Why isn't TDD more popular in universities? Asked 2020jan01 view 24k times - softwareengineering.stackexchange.com

Recently, a person here asked a basic question about how to compute in Python all permutations of elements from a list. As for most questions asked by students, I haven't provided the actual source code in my answer, but rather explained how to approach the problem, which essentially ended up in a basic presentation of test driven development. ...


Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.