One of the first issues when using the Pi, which I intend to use with Python for projects using the GPIO pins, is to decide whether to use the standard RPi.GPIO library or the newer, more user friendly GPIO Zero, which has been written as a “language wrapper” and removes some of the complexity. Both control how the Pi’s GPIO pins are designated and used in various circuits. Having recently gotten a Freenove starter kit for Pi, the documentation comes with code examples written in both C, which I’m not interested in learning right now, and two versions of Python, one of them focusing on Zero, and the other in regular GPIO, the circuitry and components being identical in both cases.
I notice immediately that the Zero code tends to be more concise and easier to follow. The Zero libraries force the use of the Broadcom numbering system (BCM) for the IO pins, and makes the assigning of pins and designating them as input or output simpler. It allows you to import specific libraries for all common components you’d normally use, so for example you don’t need to manually designate the pin connecting to a LED as an output like you would in GPIO, since it will always be an output. Also you don’t need to manually tell the Pi to set the pin on a button to use a pull up mode, Zero’s designated button library takes care of it automatically. So Zero takes away some of the repetitive busy work you need to do in GPIO. I do wonder though if there might be more value in sticking with he standard GPIO, as it might give a slightly deeper understanding of what is happening with the board and its pins. Also, what if at some point I decide to use some component for which a specific Zero library hasn’t yet been written, and have become too used to the training wheels always being there?
I think for now I will stick with learning using the Zero Library, and when i get more familiar with the various circuits can always go back and dig deeper into the GPIO coding.
Interestingly the Freenove kit comes with a manual specific to the Processing language, with variants of the circuit-specific programs in Python. As well as controlling the components on the breadboard, this provides an interactive sketch on the computer screen, allowing you to use mouse and keyboard inputs to manipulate graphical elements on the screen simultaneously. So far the sketches have been controlling the components, but it’ll likely be possible to have this work the other way round as well. It could be a very interesting way to generate imagery on screen by manipulating real world objects.
Project by project notes, comparing Python to Processing approaches:
01_1_1 Blink – get an LED to blink on and off every second.
Just a red LED connected from a pin (designated as an output) in series with a 220 ohm resistor, the other end to ground. Both Processing and Python need to import an IO library initially. Processing imports “processing.io” and Python imports “LED” from “gpiozero” in this case. Python uses a while loop (while True:) with a simple code block which turns the LED on and then off (led.on and led.off) and just waits for the user to quit out of the code with the CTRL + C shortcut. It uses a “time” function from “sleep” to give a delay of 1 second between changes “sleep(1)”.
Processing works things slightly differently, using its “draw()” loop to cycle the LED on and off. It also sets up a boolean variable to identify the on or off state of the LED. (I was reminded that in Processing, unlike Python, you have to declare the type of variable you are using “boolean ledState = false;”) At the top of the loop the code inverts the ledState and then uses a if/else block in to set the output pin to high or low depending if it’s true or false. Each time the loop is run the state changes. In the “setup” block the “frameRate” is set to 1, which provides the timing. A small box is drawn on the screen which is set to change colour when the LED state changes.
A variant of this is then introduced with the same circuit, and a small change made to the code, which takes the “ledState = !ledState” inversion code from the top of the draw loop and puts it in a separate “mouseClicked()” block, activating the LED change when you click on the square. This is obviously why they put the ledState variable in as a Boolean in the first place. Neat.
Get a button to switch on a LED when pressed.
Adding a switch into the circuit makes the arrangement a fair bit more complex. As well as needing the 220 Ohm resistor connected in series with the LED, there are 2 x 1K resistors connected to the switch, one connecting it from 3.3V power back to ground, and another connecting that loop to the input pin. This apparently is a “pull up” resistor which stops the pin from giving innaccurate values, also apparently limits the current to the pin to protect it.
There are some differences between how the GPIOZero and the GPIO versions work. The GPIO version has a setup function, in which the pins for the button and LED are designated, and the button pin set to a pull up mode. Then a loop function uses a while True statement to monitor the button’s input pin and set the LED pin to High when it is pressed down.
The code is initiated with:
if __name__ == '__main__': # Program entrance print ('Program is starting...') setup() try: loop() except KeyboardInterrupt: # Press ctrl-c to end the program. destroy()
This is what calls the setup and main loop, and waits for the KeyboardInterrupt to invoke the “destroy” function, which closes the program and uses GPIO.cleanup to presumably to set everything back to a default state. By the way, that code snippet above was formatted by enclosing the text in <pre> and </pre> tags, otherwise the browser strips out the whitespace. Nice.
The Zero version is predictably shorter, since the LED and Button libraries make assigning them their pins easier, and automatically sets the button pin to pullup mode. Then there are 2 functions, onButtonPressed() and on ButtonReleased() which simply set the LED to on or off.
button.when_pressed = onButtonPressed
button.when_released = onButtonReleased
These two lines call those functions, and instead of a while loop, the loop seems to be maintained by a pause() statement at the very end, without which the program just ends. There is no Processing version of this circuit. What confuses me about this circuit is why it is necessary to have the LED connected to both a 3.3V power source, and the output pin as well. Would the voltage from the pin not be enough to switch the LED on?
This project uses the exact same circuit and components as the previous one, but now the button toggles the light on and off with each press.
The GPIO code sets up a Boolean variable called ledState and sets it initially to False. Then a while True loop detects the button press with this code:
GPIO.add_event_detect(buttonPin,GPIO.FALLING,callback = buttonEvent,bouncetime=300)
The Falling command detects when the voltage begins to change on a button press, and the bouncetime eliminates the “jitter” in the voltage when the switch changes from one state to another which apparently happens in components before they settle to the new value. This then calls the buttonEvent function which inverts the ledState Boolean and changes the led output.
The GPIOZero version comes in two types, both importing LED and Button. The slightly longer one uses the led.toggle() function to switch the LED on or off on detection of the button press, which does away with the need for a Boolean tracking the ledState. What I don’t understand is that within the button detection loop it uses a sleep function from time to give a one second delay in the while True loop:
def loop(): #Button detect button.when_pressed = onButtonPressed while True: time.sleep(1)
This appears to have the same effect regardless what it is set to, or if a random print command is stuck in there. Maybe it’s a kind of placeholder since the loop can’t be just empty? It works just as well with a pass command. Weird. The simplest of all the programs just uses a pause() function, from signal at the very end of the program. This seems to be the only thing maintaining the loop. According to the docs the signal.pause() function “Cause the process to sleep until a signal is received; the appropriate handler will then be called. Returns nothing.”
03_1_1 Flowing water light
This circuit makes use of the bar graph LED component, which has a stack of 10 red LEDS in a neat row, Knight Rider style. Each one is driven from the 3V3 power via a 220 Ohm resistor, and then back to a pin, so the wiring is pretty simple. Although the manual says In this circuit, the cathodes of the LEDs are connected to the GPIO, which is different from the previous circuit. The LEDs turn ON when the GPIO output is low level in the program. Will just have to get used to that setup.
GpioZero has a specific Ledboard module. The code has a list called ledPins which has the pin numbers of each LED, and a while True loop uses a for loop to cycle the LEDS on from first to last, turning each on for 0.1 seconds then off again (sleep(0.1) ) and then another to switch them off in reverse. There’s very little difference in the GPIO version.
Processing makes this circuit a lot more interesting though. Written as sketch 02 1 1, the program draws a series of rectangles on the screen corresponding to the LEDS on the strip. A module called processing.io is imported to handle the io pins and as before a list (array) is given to define the 10 pins. The draw loop uses mouseX to give the horizontal position of the mouse, and with each iteration of the loop checks it against a multiple of the width of the rectangles. If the mouse is over a rectangle, it both fills the colour as red and switches the LED on with GPIO.digitalWrite(leds[i], GPIO.LOW (Low is on in this configuration.) The else part of the loop fills the colour white and leaves the LED off.
It’s not hard to see now how Processing could give you a lot of scope to have visuals control components and vice versa.
04_4_1 Breathing LED
The next project introduces the concept of analogue signals from the Pi using PWM (Pulse Width Modulation). This is a way for a digital output pin (GPIO18 on the Pi) to achieve the effect of an analogue voltage by outputting a series of digital pulses, and varying the duration of these such that pulses of longer duration will produce a higher “analogue” voltage than those of a shorter duration when generated at the same frequency. From the manual – The total time for each set of high levels and low levels is generally fixed, which is called the period (Note: the reciprocal of the period is frequency). The time of high level outputs are generally called “pulse width”, and the duty cycle is the percentage of the ratio of pulse duration, or pulse width (PW) to the total period (T) of the waveform.
It’s also explained that while C has both a hardware and software solution to achieving PCM, Python only has the less accurate software solution available.
The circuit is very simple, just a LED connected from GPIO18 back to ground in series with a 220 Ohm resistor. The GPIO code has the usual setup section, setting the pin to output mode and giving an initial LOW level to turn off the LED (still not sure which way round this is half the time.) This bit of code p = GPIO.PWM(LedPin, 500) sets the frequency to 500Hz, gives a nice smooth transition from bright to dim, reducing it a lot gives a very noticeable flicker. Then a while True loop uses two for loops which incrementally increases the range from 0 to 100 and then back again with a ChangeDutyCycle() function.
GPIOZero imorts a PWMLED function and essentially does the same thing. There is a difference in how the values are handled in this loop:
for b in range(0, 101, 1): # make led brighter led.value = b / 100.0 # set dc value as the duty cycle time.sleep(0.01)
I wasn’t sure why the value in the loop was being divided by 100, so by tweaking the values I discovered that the number given for led.value must be between 0 and 1 or it crashes the program, so it’s just catering for that.
Processing handles things quite differently, and changes the output pin form GPIO18 to GPIO17 for some reason. This is confusing as 18 seems to be the dedicated PWM on the Pi, and along with the processing script there is a class on a second tab called SOFTPWM which is called on the main sketch, which designates pin 17 as the PWM pin, and the code within can set any pin as a PWM (including 18!!!) I don’t yet understand why it doesn’t just use 18 and take advantage of the Pi’s hardware.
As an on-screen sketch the Processing version draws a circle which turns a brighter red as the LED brightens, and a bar with a slider which counts from left to right in time with it as well. Left alone these will cycle from dark to bright by themselves, but if the mouse is moved over the bar the horizontal position will control the LED brightness. Since this is the first sketch which gets a bit complex, I’ll do a step by step review of the code since I’m a bit rusty on aspects of Processing:
Pre setup, processing.io is imported, the key variables are initialised and an instance of SOFTPWM() is called with SOFTPWM p = new SOFTPWM(ledPin, 10, 100) These 3 arguments passed in designate pin 17 as the PWM pin, and set the duty cycle and period. The function corresponding in the class is SOFTPWM(int iPin, int dc, int pwmRange) I don’t understand the class code well enough yet, but both the int dc and int pwmRange get multiplied by 100,000 in there possibly as we’re dealing with very small increments of voltage and frequency. The draw loop has an if/else statement which in the case of mousePressed reads the position of mouseX, and uses map() to produce a value between one 1 and 0, stored as variable t. This value is fed into the p.softPwmWrite() function from the SOFTPWM() class and sets the brightness of the LED. It is then used to set the fill colour of the ellipse, by altering the value of the green and blue parameters while leaving the red at full strength 255. A progress bar is drawn, with t used to set the x position of the end point. Finally a counter is printed above the progress bar showing the percentage brightness by multiplying t by 100, and nf() is used to format the numbers into a string to be printed on screen. PushMatrix() and popMatrix are used after the ellipse is draw, to present the bar graphics. I assume this is to prevent weird behaviour of the animated elements on the canvas, but I notice the sketch works fine without them anyway.
If mousePressed isn’t active, the initial draw loop activates the else scenario, which simply increments t steadily and resets it to 0 when it reaches 1, so the animation and LED brightness just loop endlessly.
This circuit introduces a RGB LED, capable of mixing the 3 primary colours of light to produce theoretically any colour. It has 4 pins – 1 common anode (longest) and three others as cathodes for each primary each connected back through a 220 Ohm resistor to a pin. GPIO sets up similar to before, except 3 pins are designated as outputs and assigned t each leg of the LED. A random module is imported, and in the main while True loop a value between 0 and 100 is picked for each colour, and by calling a setColor function these are written to the LED with ChangeDutyCycle(). Using sleep() a one second delay is used until the next change.
GPIOZero imports a specific RGBLED module which can handle all the inputs, but otherwise is structured the same.
Processing now gives us three bars which function as sliders to individually set the brightness of each primary colour. The three pins are set up, again using the class SOFTPWM which appears to be identical to the last project. The job of generating the progress bar is now farmed out to a separate class ProgressBar, presumably taking less code than dealing with three different ones in the main sketch. Three of these bars are initiated, one for each primary. Interestingly, the main draw() loop now only draws the circle and fills it with the appropriate mix of colour, and draws the progress bar on screen. There are then 3 separate functions for mousePressed(), mouseReleased() and mouseDragged() which handle the mouse behaviour. mousePressed() checks to see if the mouse is clicked within any bar’s boundary, and if so sets a boolean (rMouse, gMouse or bMouse) to true. Then in mousedragged(), 3 if statements only act on changing the LED state if the corresponding boolean is true, and if so updates the LED colour and progress bar. mouseReleased in turn sets the 3 mouse booleans back to false, so mouseDragged doesn’t get called.
06_1_1 Buzzer / Doorbell
A nice little project which connects an active buzzer to a switch, and the buzzer is driven by a npn transistor connected to a 1 KOhm resistor. When the button is pressed, its input pin sends a signal to the output pin of the resistor, which then sends a current to the base of the transistor, acting as a switch to allow current to flow from the collector to the emitter, sounding the buzzer. The code used for this is basically the same as the push-button switch from earlier.
A pnp resistor works differently and seems to invert the signal.
The Processing variant of this uses the same circuit but with the switch now removed. A buzzerState Boolean is declared, the state of which is altered in a mouseClicked() function. The main draw loop reads this value and if true sets the buzzer pin high with GPIO.digitalWrite(buzzerPin, GPIO.HIGH); and then calls a drawArc() function to draw a little graphic on the canvas to indicate the buzzer sounding. No need to involve any classes for this one.
A variant of the previous project which swaps out the active buzzer for a passive one (the active one has a sealed in underside while in the passive one you can see into the component). The sound now produced varies in frequency like an ambulance siren. The passive buzzer must be fed a PWM input signal to control the sound output, and this is handled by designating the output pin as a PWM, and feeding a sine curve into the buzzer while active. The math module is imported to do this. Apparently the active buzzer has the necessary stuff built into it to produce the sound waves, while the passive requires it to be fed in. There is no Processing version of this project.
07_1_1 Read Voltage of Potentiometer (Analog Digital Convertor) and 08-1-1 Softlight
Things getting tricky now. This is the first use of an IC (integrated circuit) in this case an ADC (analogue digital converter) which will take an analog voltage from a potentiometer, or variable resistor, and turn it into a digital value. The IC used here is called the ADS7830, which has 16 pins. The 8 pins on the left can all read separate analog inputs. Of the remaining pins which are used in this project, there is a ground and a 3V3 power supply, and 2 others which communicate with the pi via I2C (Inter-Integrated Circuit). The serial data and serial clock lines are used to achieve this, (SDA and SCL respectively.) Pins 3 and 5 on the pi correspond and connect directly to these. The I2C interface is disabled by default on the pi, and needed to be enabled in the settings, and some software installed to run it. Then the command i2cdetect -y 1 is used which gives the address of the connected device. Finally, for Python, a custom module needed to be installed, ADCDevice, to allow Python code to work with the ADC.
Having done all this the GPIO code is quite simple. An object named adc is initiated from the ADCDevice class, and a while True loop reads the voltage from the potentiometer with value = adc.analogRead(0). Then voltage = value / 255.0 * 3.3 is used to get the voltage from this, as the ADC is an 8 bit device, it can handle up to 256 values. Then both digital and analog values are printed to the screen. The GPIOZero code seems to be the same in this case. There is a Processing version of this project, but unfortunately the code seems to be broken and won’t detect the ADC. Since there are 2 versions of the ADC which can potentially ship with the pack, all of the code versions have an if statement to choose one or the other based on the particular address used. The Processing version, quite rightly, finds one and not the other, and seems not to be satisfied with missing one of them. With a bit more knowledge later I might be able to deal with that.
The Softlight version of this project is essentially the same, but adds an LED into the circuit connected to an IO pin, which turns the brightness from off to maximum when using the potentiometer.
Update – eventually managed to fix the Processing code by changing an index value in the ACDDevice class, and after a bit of fighting it worked. The sketch has 2 additional tabs, one for the ACDDevice class and the other for the SOFTPWM class. The top of the sketch initialises an instance of each and assigns them variable names, setup calls the .detect method on the ADC class to return which brand of ADC IC is present (still generates an error because it can’t find one of them but at least it works now.) In the draw loop the .analogRead method is called and returns a number between 0 and 255 from the potentiometer. A value for volt is calculated with float volt = adcValue*3.3/255.0; Both of these get written to the screen, and a bit of maths done to volt to get a value to send to the .softPwmClass method of the PWM class to set the LED brightness. The same number is used to calculate the fill value on an ellipse drawn to the canvas so it turns from white to red as you rotate the potentiometer.
09_1_1 Colourful Softlight
This is a variant of the colourfulLED project above, except now instead of having the script generate random values for the RGB components of the light at set intervals, each of the primary colours has got its own potentiometer allowing you to manually mix the light levels to produce the desired combination of them. The GPIO version behaved a bit oddly, but the GPIOZero script worked very well with the circuit. The circuit is similar to the Softlight one above, except now the single LED becomes an RGB LED and there are 3 potentiometers in total. These now use 3 of the input pins on the ADC converter, whose values are read and fed to 3 IO pins which connect to the LED. GPIOZero imports its own RGBLED library and assigns it its IO pins. Then an instance of the ADCDevice class is created to handle the ADC convertor. A while True loop reads each of the 3 values from the potentiometer and uses RGBLED to feed them to the IO pins. Also the 3 values are continuously fed to the screen. It’s quite satisfying to be able to mix the 3 colours together with the analog dials. Processing doesn’t have a sketch to use the potentiometers but this behaves exactly as the project 05_1_1 ColourfulLED above where 3 sliders were used on screen to set the RGB values, so there’s a nice comparison there between the two approaches.
10_1_1 Photoresistor & LED Nightlamp
This project is very similar to the Soft Light project above, except that instead of a potentiometer, a photoresistor is used in combination with a 10 KOhm resistor to vary the voltage coming into the ADC. A while loop reads this value and uses the ChangeDutyCycle method to set the brightness of the LED.
11_1_1 Thermistor Thermometer
The circuit here is similar to the last except the LED is removed and the photoresistor is replaced with a thermistor to vary the voltage read by the ADC. There is a bit of maths done to this value to convert the value to degrees Celsius and the ADC reading, voltage and temperature are printed off to the screen. There is a Processing version of this project too, which just prints the values to the canvas from the draw loop.
This circuit is similar to the previous ones using multiple potentiometers except replaces them with a joystick, described as 2 rotary potentiometers set at right angles to each other, which feeds values to the Pi via the ADC. As well as power and ground, it uses a GPIO pin each for the x and y axes, and another for the switch action when you click the joystick button. This one can be connected directly to a GPIO pin as it’s a digital signal. The GPIO code works much as before, with the values being read and printed on screen continuously. There is a Processing version too, which moves a dot around the canvas to represent the position of the real joystick.
Etch A Sketch project
At this point I decided to have a go at making an Etch A Sketch plotter with two potentiometers to apply some of principles learned so far.