Create a Control Panel Using a Raspberry Pi

Steve Harding
10 min readDec 30, 2021

--

Clock and controller: Sound up to hear it clunk! The controller is on the table below the clock.

I have been building Raspberry Pi devices for several years now. Often I build something, admire it for a few days, show it to my friends (who think I’m crazy for making these things), and eventually tear it apart. But I am always looking for a project that I might keep intact. This story is about one such project, namely a Raspberry Pi based clock controller.

I recently purchased a vintage IBM Clock for my living room wall. I knew when I got it I would need to make an electronic device to control it. I describe the clock and my plans for it in a different Medium story published recently. And to finish up the clock story, I added a chime (actually a cuckoo) to it. And of course there’s a story about that addition if you care to read it.

To operate the clock, I designed and programmed a Raspberry Pi-based controller. I realized that the clock controller uses techniques that are generally useful for controlling devices other than clocks. The controller implements an interactive button panel using event-driven I/O and multi-threaded processing.

None of the techniques I use are novel and they are explained by many tutorials on the web. I used these tutorials to learn about these techniques. But tutorials typically teach concepts in isolation from each other. This controller project demonstrates how concepts can be combined to perform useful functions.

So even if you never refurbish an IBM Clock, you might find the controller program interesting. The Python code is available on my Github account for you to read or download.

Experimentation with the code is encouraged! The hardware design is very simple. To run the code all you need are five pushbuttons, five LEDs, five resistors, and a Raspberry Pi. The clock and relay are not necessary to just run and tinker with the code.

Note to the reader: I am not providing step-by-step instructions for assembly, code download, Raspberry Pi setup, etc. I assume that if you want to run this code you are an intermediate/advanced level Raspberry Pi user who can use what I write here to get this running.

The following describes the controller in the context of running my clock. My needs are to turn a relay on and off in certain ways. The controller modes that I describe can be changed for other devices.

Requirements

  1. The clock controller needs to provide one 24 vdc pulse per minute. Each pulse needs to be held for one second to assure that the clock mechanism has time to operate properly. When operating in this manner, the controller is said to be in Normal Operating Mode.
  2. The clock controller needs to be able to stop Normal Operating Mode for a variable amount of time up to one hour. This is called Stopped Mode. Stopping the clock for an hour is useful for setting the clock behind an hour for Daylight Saving Time. It is also useful when manually setting the time.
  3. The clock controller needs to be able to deliver pulses relatively rapidly to step the clock forward. This is called Rapid Mode. When in Rapid Mode, the clock can be stopped by manually entering Stopped Mode. A special case of Rapid Mode is to automatically stop sending pulses after 60 minutes have been added to the clock. This is useful when setting the clock ahead for Daylight Saving Time. It is also useful when manually setting the time.

These three requirements together provide an operational clock that can be manually set to the correct time with relative ease.

The clock in Repeat Mode…this is how to set the clock forward. The red button stops it.

Raspberry Pi Clock Controller — Hardware Selection

My choice of name for the controller says a lot about the hardware I used! Clearly I based the controller on a Raspberry Pi. Since the demands are not processor-heavy, a Raspberry Pi Zero will do nicely. I set this up using SSH, so a Wifi connection is needed. I also may decide to add a web interface, which is another reason for Wifi connectivity. Therefore I chose a Raspberry Pi Zero W for my processor.

The requirement for 24 vdc pulses means a 24 volt dc power source is needed.

A relay is needed to allow the Pi to turn the 24 v on and off using GPIO.

The requirements for different operating modes mean that some kind of control panel is necessary. I decided to use five momentary push buttons with five colored button caps to give the user control over the clock modes.

Associated with the buttons are five LEDs of matching colors. These are used to give a visible display of the current operating mode.

To limit current draw through the LEDs, five 330 ohm resistors are needed.

To interconnect the various components I chose the simplest, and least reliable, option: a pluggable breadboard.

Wiring is done using a large handful of pluggable jumper wires.

Controller Packaging

With five buttons, five LEDs, and a relay, packaging is an issue. For this contraption to work reliably there needs to be some sort of frame. Everything cannot just flop around on the table!

I used a small piece of flat wood, few other wood parts, some screws, and some tape to mount the breadboard and Pi, the relay, and the five buttons. The LEDs and resistors are just plugged into the breadboard. Pluggable jumper wires connect everything. This is good enough for now.

Controller Assembly-Pi is on the left; Buttons, LEDs, & breadboard in the center, relay and 24 vdc plug on the right

Controller Wiring

The wiring for the controller is easily conveyed without a full schematic because it is so simple. Broadcom pin numbering is used.

Each pushbutton has one terminal connected to +3.3 v on the Pi board. The other button terminals are connected to the pins listed below.

Each LED has a 330 ohm current-limiting resistor connecting its cathode (the short pin) to ground. The anodes (long pins) of the LEDs are connected to the Raspberry Pi pins listed below.

Raspberry Pi Pin Assignments for LEDs and Pushbuttons

The relay I use is connected to +5 and ground and has a trigger pin. Pin 5 that is connected to the white LED anode is also connected to the trigger terminal of the relay. This means that when the white LED is turned on by a GPIO call, the relay will be energized as well. The LED provides a visual indication of when the clock should advance. When the LED turns on and the relay engages, 24 vdc energizes the clock mechanism and a pawl moves into position on the minute hand gear. When the LED and relay are turned off by the controller after one second, the mechanism advances the minute hand one minute and the pawl is positioned for the next pulse. Normal Operating Mode causes the white LED and relay to cycle in this manner once a minute unless stopped by a press of the red button.

Pressing the white button while in normal operating mode will cause the clock to advance one minute for each press of the button. I call this Plus One Mode. You do this to fine tune a clock setting when the time is close but not correct.

The other LEDs provide visual feedback for long-running features of the controller. The blue, green, and yellow LEDs are lit when the clock is advancing in a Rapid Mode.

Rapid modes are used when you want to catch the clock up more than just a few minutes. The code is written so that blue mode moves ahead 30 minutes, black mode (signaled using a green LED, because there is no such thing as a black LED) moves ahead 180 minutes, and yellow mode moves ahead 60 minutes. Yellow mode is how you adjust the clock for Daylight Saving Time in the spring.

The red LED provides visual feedback that you are in Stopped Mode, which is entered by pressing the red button. Pressing the red button again ends stopped mode, i.e. the red button toggles stopped mode on and off. If you enter stopped mode and do not toggle it off, it will turn itself off after 60 minutes. This is how you adjust the clock for Daylight Saving Time in the fall.

The Clock-Runner Python Program

The clock controller program is interesting because its design shows how to use GPIO event-driven I/O along with Python’s threading library to provide a usable and responsive button-driven control panel. The principles shown by this clock controller can be used by other projects that need a button control panel.

As mentioned earlier, the code for the clock controller is available on Github for you to examine or use. If you want to experiment with it, you will need to use my description of the hardware to build the controller. A subset of the controller buttons and LEDs is possible for tinkering. You should at least implement hardware for the white, blue, and red channels. The relay is not needed for tinkering.

I made a short video showing how the clock controller operates. I describe what is happening in the paragraph below this video.

Demo of the Button Control Panel

Control Panel Demo

  1. With no buttons pressed, the white led lights and the relay activates for one second. This is Normal Operating Mode, one pulse a minute, forever.
  2. After the first pulse, I press the white button twice. Each time causes Plus One Mode to run. Each press adds one minute to the clock.
  3. Next I press the blue button. This causes the blue Rapid Mode to run. The blue led is lighted to tell the user which mode is active. If not stopped by pressing the red button, blue rapid mode adds 30 minutes.
  4. During the time that blue rapid mode is running, I press the red button to enter Stopped Mode. Blue rapid mode stops. Stopped mode is ‘sticky’ in that it remains active until red is pressed again. This is only partly true because stopped mode will time out and reset itself after 60 minutes. This is how you adjust the clock to lose an hour for Daylight Saving Time.
  5. While in stopped mode, I try pressing other buttons. They are disabled because the system is stopped.
  6. I press the red button again, exiting stopped mode.
  7. I press a few other buttons, showing that button selections are working again.

Discussion

It seems like it should be easy to implement a pushbutton control panel. Most of the GPIO tutorials would make you think so. Sure, just set up the buttons for GPIO input, test each one in a loop until one is pressed, and call a function corresponding to the button to do something. Loop back and scan the buttons until the next press. And so on…

A complication is that your user will expect to be able to press any button at any time, even when another button press has started your program doing something that does not finish right away. As long as that ‘something’ is underway, your program will be unable to sense that another button has been pressed.

Another issue is that looping through the buttons waiting for one to be pressed is just bad. This is called polling. It ties up the main thread of execution just to watch for user input. For the clock program, I need the main thread to loop, generating one pulse per minute to advance the clock. So polling the buttons on the main thread is impractical. An inelegant solution would be to spin off a new thread that polls the buttons, leaving the main thread available for generating clock pulses. But this is still polling in our code, and we don’t want that!

The GPIO implementation gives us a way out of this problem using its implementation of event-driven input. This feature of GPIO allows you to configure a GPIO pin as an input pin that is monitored for a change in voltage. When the voltage changes, the GPIO subsystem ‘notices’ the change and calls a function named in your configuration for the button.

Read more about event-driven inputs here.

We hoped to avoid polling in our own code, and we can use the GPIO event processing model to do so. There is still polling occurring, but the GPIO subsystem is doing it for us. We do not need to clutter our code with a thread that watches for input. Our main processing thread is clean, and it can be used to loop, watching the system time so a clock advance pulse can be generated every minute.

There is still an issue with threading. The GPIO subsystem monitors events using a thread of its own, hidden from us. When an event is detected, say a press of the blue button, that thread is used to call our long-running function that picks the relay 30 times. As long as that is happening, no other events will be detected. What does this mean? It means that when the blue rapid mode is underway, the red button to stop it cannot be detected. But the controller requirement is that the red button always stops anything happening in the system.

To keep the red button responsive when other buttons launch long-running tasks, we need the long running tasks to run on their own threads. You can read the source code to see how this is done.

One last point is that some means of inter-thread communication is needed. Otherwise how can a press of the red button (processed on the GPIO subsystem event thread) stop a long-running task like blue rapid mode (running on its own thread)?

I have implemented inter-thread communication in a very inelegant way. There are several global variables used that can be made visible to all of the functions in the program. The best example is the variable named stopped. You can see in the code that stopped is set to be True by a press of the red button. The value in stopped is checked during processing of the rapid advance functions. When this is set to value True, the rapid advance functions stop processing and their threads terminate.

I have tried to describe why I think this device and code are generally interesting and not only interesting in the context of my old clock. I also made an attempt to describe in narrative style more or less how the code operates. Thanks for reading this, if you have made it this far!

--

--

Steve Harding

I am a retired IBM software developer. I have a passion for making creative technology projects. I enjoy writing also. Medium is a good outlet for me!