Key in the dark has been played in Scout groups and youth groups for as long as I can remember.
The idea of the game is one Scout is blindfolded and sat on a chair with a large set of keys underneath. He is “armed” with a torch. Usually a number of obstacles and places to hide are set up around them. The rest of the scouts need to stealthily get to the keys without the Scout shining the torch on them. If they are hit by a touch beam they are defined dead and have to either sit out the game, or start from the beginning again. The idea is that any sound made by the scouts in their approach means a torch shone their way.
Of course in practice a torch is not the ideal “weapon” although rules are that the torch is only to be used intermittently, it is basically an infinite resource and is usually shone around with abandon. Plus the operator being blindfold, they have no idea if the touch is on or off. The other main problem is that the torch beam diverges so much that multiple asalients can be cought at once.
So I started to look at alternatives. As 5W leds are cheap I thought of one of these as a source, combined with a 10 x 70mm focal length lens from a watchmakers eyeglass (loupe) in a 20mm tube. I could get a pretty narrow, bright beam.
Mounting the LED on a piece of copper allowed for any heat from the led to escape.
Of course, I needed a way of switching this, so I used a MC4427 1.5A mosfet driver, purely as it was a single chip solution and mainly as I had one to hand.
I dead bug soldered it, as there were only 4 connections. and glued the driver to the back of the heatsink.
I had had the idea of “charge” to keep down the number of flashes, the idea being that every press of the button would freeze out the user for 0.5 Seconds. To show this, I thought of a number of neopixels along the barrel. So these were attached with some acrylic rings to spread the light out
Whilst I was thinking about the design of this, I was given an Adafruit Feather Huzzah
This was ideal, as it had a Lipo PSU and charger built in, and plenty of IO, coupled with a 25mmx9mm LIPO battery it gives loads of power..
Plus If you use MicroPython you got the neopixel support.
Realising that the operator would be blindfolded, I wanted some feedback so they would know when the torch was used. After trying a solenoid, I found this had two problems. 1. It was power hungry. 2. It took up a load of space. So I went back to a large’ish vibration motor (also to hand)
I had decided that the whole thing should fit in a tube no more than 40mm in diameter, so I laser cut some supports and hung the whole lot from two m3 threaded bars. After many attempts I finally got the acrylic into pieces that would support the electronics and battery and assembled the whole thing together.
Of course, I’d forgotten the buttons. These needed to be at the front, but so the case could be removed they needed to connect at the back.
After a couple of attempts with internal wiring, that got cought up either putting it together or taking it apart. I eventually put the wires on the outside, covered the whole handle, wires and everything in leather and connected the buttons with a cable at the back.
The USB cable for power (and programming) currently exits out of the back, but is soon to be replaced with a micro socket. There is also a small switch for the units power.
The next addition was a burst fire mode, you get 5 shots in quick succession, but it takes longer to recharge.
I also added an auto power off, if left for 60 seconds.
Circuit Diagram
Final Touches…
End plate with USB charging port and On off switch.
Unfortunately as I take the power for the motor, Neopixels and the main LED straight from the battery Powering down is a two stage process. First you press and hold both buttons. After a few seconds everything is turned off. Then you can switch off the uP. I do it this way as I don’t have to switch everything on to charge it.
The finished Torch
Code
import machine, neopixel, time, utime np1 = neopixel.NeoPixel(machine.Pin(12), 8) np2 = neopixel.NeoPixel(machine.Pin(13), 8) LED5w = machine.Pin(14, machine.Pin.OUT) B1 = machine.Pin(5, machine.Pin.IN, machine.Pin.PULL_UP) B2 = machine.Pin(2, machine.Pin.IN, machine.Pin.PULL_UP) vib = machine.Pin(4, machine.Pin.OUT) LED5w.off() firec=(255,255,255) chargec=(0,20,0) black=(0,0,0) OffTime=60 def fire(fx): for x in range(0,fx,1): for a in range(8): np1[a]=firec np2[a]=firec np1.write() np2.write() time.sleep_ms(4) np1[a]=black np2[a]=black np1.write() np2.write() LED5w.on() vibrate() time.sleep_ms(100) LED5w.off() def charge(cx): for a in range(8): np1[a]=chargec np2[a]=chargec np1.write() np2.write() time.sleep_ms(cx) def vibrate(): vib.value(1) time.sleep_ms(70) vib.value(0) charge(63) start=utime.ticks_ms() while(B1.value() or B2.value()): if not B1.value(): fire(1) start=utime.ticks_ms() charge(63) if(not B1.value()): time.sleep_ms(1000) if not B2.value(): fire(5) start=utime.ticks_ms() charge(350) if(not B2.value()): time.sleep_ms(3000) if ((utime.ticks_ms()-start) > OffTime*1000): for a in range(7,-1,-1): np1[a]=black np2[a]=black np1.write() np2.write() time.sleep_ms(50) np1[0]=(0,0,10) np1.write() while(B1.value()): time.sleep_ms(200) start=utime.ticks_ms() charge(63) vibrate() for a in range(7,-1,-1): np1[a]=black np2[a]=black np1.write() np2.write() LED5w.off()