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.
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,))
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.
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 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, …
Appendix A – The OP’s Original Buggy Code
Appendix B – KISS
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 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
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
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. …