8 minutes
EMF Camp: Beg Button
EMF Camp1 time was rolling around again, and this time I was determined to actually bring something to show off… I’ve got my hands on and old pedestrian crossing “beg button”2 and thought it an interesting basis for a small installation to take with me
We will get back to the details of the project later in this post, but as a quick spoiler the project was a success.
The Plan
The plan was “simple”; replace the static light/display from the controller with a screen and display some interesting alternatives to the old WAIT
message.
Requirements and Stretch Goals
For the project I set myself the following requirements, in order of priority.
- Look (mostly) like an unmodified beg button when not interacted with
- Display random
WAIT
states based on UK and foreign beg button designs - Display random
WALK
states, based both on existing real displays as above, but also silly alternatives. - be triggered from the existing button in the case.
- Play sounds
- Random
- Something appropriate for the
WALK
display being shown
- Effects instead of just static images
- Rain
The Existing Hardware
The existing hardware, a module from Siemens (667/1/01478/001) is incredibly simple, the beg button is basically a very resilient case for a light bulb, a shadow mask and a push button. All the logic is handled via the road side traffic light controller, this part is essentially dumb.
Additional consideration include that the button is designed to be pole mounted, and has opening on the back where its wiring passed through.
The New Hardware
With the old light bulb and painted front “glass” removed, I could now measure everything up and find a suitable screen and computer to drive it. In the end I went with a screen from Waveshare and a pi43.
- 10.1inch Capacitive Touch Display for Raspberry Pi, 1280×800, IPS, DSI Interface
- pi4 2GB
- gpio hat
- 5V relay module
The screen isn’t a perfect fit but it’s the closest option I could find that would fit and wasn’t crazy expensive. It has a very neat mounting system with the pi4 and is all powered by USBC, dramatically simplify power delivery.
The GPIO hat wasn’t technically needed, but allowed for an easy security way of wiring everything together without the use of custom cables, and it also eased debugging with it’s status LEDS.
This project also led to the purchase of a new 3D printer…
Mounting the Screen
To mount the screen I designed and 3D printed a bezel. This was a great opportunity to learn freeCAD and this was the first model I have designed in the package. The screen does not provide any mounting points, and as such is held in place with tabs I included in the 3D printed bezel.
The Software
For this product I chose to go with the pygame framework to draw the graphics. Getting started with this framework was very quick and I had a very simple working example within a day, loading font and SVGs and bliting the parts together to form the output image. It was however very quickly evident that my prototype based on the tutorials I found was very inefficient, and difficult to expand on 4.
I also wanted if time allowed to show effects as well as just static images.
Effects
The initial plan was to use pixel shaders to create the effects however pygame’s OpenGL support is provided primarily through third party libraries and these projects are either not well documented or are unsupported. Additionally the learning curve and time set aside for this project didn’t leave much room to get up to speed with how shader languages work. Finally this is also where the choice of a pi4 bit me a bit as the Pi only supports OpenGL 2.1 and the libraries for implementing pixel shaders I looked into all target OpenGL 3.0.
Rain
The first completed effect is of rain drops falling on the walk area of the display. The rain drops will fall until they hit the bottom of the surface or collide with the icon at which point they randomly respawn at the top of the screen.
import random
import pygame
def _random_drop(width, height):
return (
random.randint(0, width-1),
(random.randint(0, height // 10)),
)
class Rain:
def __init__(self) -> None:
self._state = []
def reset(self, surface: pygame.Surface) -> None:
self._surface = surface
width = self._surface.get_width()
height = self._surface.get_height()
self._state = [
_random_drop(width, height)
for _ in range(100)
]
def _raindrop(self, xy):
pygame.draw.rect(
surface=self._surface,
color="blue",
rect=pygame.Rect(xy[0] - 1, xy[1] - 10, 3, 10),
)
def draw(self):
width = self._surface.get_width()
height = self._surface.get_height()
for i in range(len(self._state)) :
point = self._state[i]
point_y = point[1]
if point_y >= height or self._surface.get_at(point) != pygame.Color("BLACK"):
self._state[i] = _random_drop(width, height)
self._state = [(xy[0], ((xy[1] + 3))) for xy in self._state]
for drop in self._state:
self._raindrop(drop)
return self._state
Sounder
The crossing box did come with the original sounder, this “pips” each time a voltage is applied, so needs to be pulsed to make the expected sound5. The unit is rated at 6v to 36v and after a bit of playing the volume looks to be directly related to the voltage applied. Around 12v was found to be a good volume but to do this I would need a 12v rail and some way of switching it as the pi and it’s screen run on 5v.
There are two options for this extra rail as I want to continue to use USB to power the device. Use a USB PD Decoy to get 12v from my USB power supply then down convert this to 5v for the pi. Or continue to run the pi at 5v and use a USB to 12v step up converter. Being unsure I bought components to do either way.
Driving the sounder was implemented though a relay controlled by a Pi GPIO pin which allowed me to easily switch the 12V supply.
The sounder work was somewhat waylaid by me ordering a 12v relay and trying to switch it with 5v… And the sounder being broken and not working reliably at 12v (well within it’s rated voltages) and in the end didn’t make it to EMF.
In the end I didn’t use the sounder as it wasn’t reliable at 12V even when run off my desktop power-supply. I expect if I want to include a sounder I will have to go hunting for a replacement module.
Speaker
My initial plan was to add a speaker to play crossing sounds from around the world however it’s been quite difficult to find high quality recordings of these without large amounts of road noise.
pygame’s audio support made playing a random sound file whilst the crossing walk state is active easy.
In the version of the project that made it to EMF Camp a battery powered portable speaker was glued inside the case and played the sound.
Final Thoughts
Overall I am happy with this a project, people interacted with it and asked questions. Some kids must have pushed the button 50 times. A few people even tooted about the project (OK they were replying to me asking for feedback but it was still nice).
@Mexicals we had “hike” so went on a wander instead of going to bed 😅
@Mexicals omg I pushed this button, highly recommend! Where did you get the housing?
There were a few issues, I had made a code change just before I left for the field, which cause the app to not start on boot-up, and crazily no one I asked had a spare USB keyboard I was able to borrow. But once a keyboard was found and the crossing joined to the WIFI network is was nice and easy to remote in and fix the issues.
Immediate next steps will be to print a wall mount for the box so I can have it set up more permanently in my shed. Beyond this, I would like to add some extra features such as weather adaptive effects, and include some recording functionally to track the number of times the button is pushed. I would also like to find a more final solution for the speaker/sounder. I might even re-write everything in a more performant engine as an excuse to learn a new tool. I also now have some ebay searches set up for more street furniture… Traffic lights next time maybe?
-
If your interested in EMF I wrote a post about EMF2022 previously. ↩︎
-
So called because pedestrians have to press the button to beg the traffic to stop and allow them to cross. ↩︎
-
Going with a pi4 for convenience may not have been a great idea in hindsight, I didn’t realise how weak it’s GPU really was. ↩︎
-
If you are really interested all the history is laid bare in the GitHub repo, but it’s not pretty. ↩︎
-
Correction, the sounder beeps all by itself when enough voltage is applied, it’s just broken at lower voltage. I’ve left the narrative here as how I thought but we will come back to this later. ↩︎