Commence Primary Ignition Hey Everyone, We're playing with lasers! This comes with a safety warning. I'm using a low powered 5 milliwatt laser
diode that you would normally find in a typical laser pointer, but there are some extremely
powerful lasers available for sale online. Lasers are dangerous and come with
potentially life-changing risks. Do not look directly at the laser
and beware of reflective surfaces. Having said that, if you are careful and stick
to low-power devices, you can have a lot of fun. In this project I'm using a laser galvanometer
often shortened to just laser galvo. You can find these on eBay for between 70
to 80 pounds which is around 100 dollars. Looking closely at the device we can see that
it consists of two mirrors at right angles. Shining a laser beam at one of the mirrors lets
us direct the beam in the X and Y directions. I've linked to a great video in the comments that tears down various galvo motors so you can see what's inside. The basic operation is a magnetic core with coils coupled with a feedback mechanism to sense the rotation of the shaft. In this image we can see the feedback mechanism. They have an infrared diode with two detectors. As the shaft rotates the amount of
light falling on the detectors changes. The driver circuitry is pretty complicated
with a lot of non-linearity so it's best not to mess with all the potentiometers as
these are normally calibrated at the factory. To interface with the driver board we need to provide a differential signal with a
peak-to-peak value of minus 5 volts to plus 5 volts. We also need to provide samples
at a sufficient sample rate to move the laser fast enough to take
advantage of persistence of vision. I'm going to be using 20KHz which seems to
be what my boards have been calibrated at. Now, normally we'd use I2S for outputting samples
at this rate, but we have some extra complexity. We need to turn the laser off and on
synchronously with sending samples out. I2S uses DMA so this would be quite difficult
to do without jumping through a lot of hoops. I2C is not really fast enough so we're
left with SPI as the best option. So, I'm going to use the MCP4822
which is a dual-channel 12 bit Digital to Analog Converter with an SPI interface. This will output a value between 0 and 4.096
volts - so we'll need some extra circuitry to convert this to the differential
output required by the driver boards. Let's have a look at the schematic. Now, I've had this board manufactured as a custom
PCB, but you can easily build this on breadboard. We have the power supply coming in. I'm using the power supply that
comes with the laser galvo kit and I've added an extra header so we
can daisy chain devices on top of this. This gives us a plus 15 volts,
ground, and minus 15 volts. I'm passing the input voltages through an LC
filter to remove any noise - this is probably not really necessary and i'll replace it
in future versions with a simple choke. To power the DAC we need 5 volts. This will let us give it the full
output range of 0 volts to 4.096 volts. I'm using a 5 volt Low Drop Out regulator here. We don't need much current
so this won't get too hot. Here's the DAC circuit. It's pretty simple we have a decoupling capacitor
and a couple of filter capacitors on the outputs. These help to remove any clock noise that
may come through from the digital interface. We can now move on to processing this signal
into a differential signal to drive the galvos. The first thing we'll need is a
voltage reference of 2.048 volts. You could use a voltage divider
here, but I'm using an ADR5040 which will give us a reference
voltage of exactly 2.048 volts. Before i use this reference voltage I'm passing it through a voltage
follower so that it gets buffered. We pass the left and right channels
from the DAC to a voltage subtractor. This subtracts the voltage reference and
also amplifies the signal by a factor of two. The output of this gives us the positive
side of the left and right channels. We feed these two signals
into an inverting amplifier with unity gain and this gives us the
negative sides of the left and right channels. That's the analog side of our circuitry done. To control the laser we use a simple
MOSFET circuit when the LON signal is high the laser will be turned on and when
it's low the laser will be turned off. JLCPCB are now SMT assembling ESP32 modules
so I've added a WROVER module to my schematic. To power this I've got a 3.3 volts regulator. I'd normally use something like the AMS-117
which can handle the 15 volts input, but instead i'm trying out one of these little
switch circuit modules that are pin for pin compatible, are much more efficient,
and don't generate quite so much heat. The wiring of the module
is pretty straightforward. I'm not doing anything fancy with the RTS and
DTS lines so programming is a case of tying IO0 to ground and then power cycling. JLCPCB have just announced support for USB
connectors so I think for version two of this board we'll probably just include
a USB socket and a USB to UART chip. The PCB layout is pretty simple. I've put the DAC very close to the ESP32 and tried to keep the analog
circuitry away from the digital. I've ended up with a four layer board. I did manage to route it on two
layers, but it was pretty horrible and the price difference for
a small run is not that much. The top layer contains all our signal traces. The next layer contains a ground plane and
I've used the other layers as power planes. Having four layers does massively
simplify the layout process. Here's the completed circuit board from JLCPCB. Now, the observant of you may
notice a small bodge wire. My first revision of the schematic
had a bit of a stupid error. I thought I'd wired pin 3 on
the voltage reference to pin 1 which is what the datasheet recommends to do. But I hadn't actually connected the
wires - the red circle was missing. Unfortunately this didn't get
flagged up by the software as all the pins were connected,
but luckily this is an easy fix. On the software side I've written a very
basic laser show player I'm using PlatformIO for this project and the ESP-IDF, but
the code should work on Arduino as well. There is a standard format for laser
shows which has been defined by the International Laser Display Association. Reading these files is reasonably
straightforward and we can use similar code to what we use for reading WAV files. We have an ILDA header structure and
we have an ILDA record structure. These can both be read directly from the file. The only thing we need to check when reading these
structures is that the byte ordering matches. To actually read the file, we
first read in the first header. This tells us how many frames are in the file and then the file just consists of multiple
ILDA headers followed by multiple ILDA records. To fit more data onto the SPIFFS
partition I'm gzipping the ILDA files. To decompress these I'm using code from zedlib. I have a simple wrapper around this that will
read uncompressed data from the gzip file. When we open the file we read a small
chunk of data and initialize zlib. And then to read data we tell zlib how
many bytes we want and where to put them. We then repeatedly call inflate until it has
decompressed the required number of bytes. If needed we read in more data from the file. With the data read into memory
we can now play the show. We have two output pins - the
pin to turn the laser off and on, and the pin to tell the MCP4822
to output the new voltages. We use this to make sure that the left and
right channels are updated simultaneously. We set up the SPI device with the required pins and then kick off a timer to
actually output the samples. In the timer we send each sample out
using "spi_device_polling_transmit" This is the fastest way to send
out SPI commands synchronously. We set the state of the laser
depending on the instructions in the ILDA file and then we pulse the LDAC
line low to output the new voltages on the DAC. So, that's our laser show done
all the code is in GitHub. This is a very basic implementation. There are a
lot of things that could be done to improve it. We're limited on the length of show
by storing the ILDA files in SPIFFS. It would be much better add support for
an SD card and read the files from there. We could even stream the files from a server. I'm using a WROVER module and I've configured
the IDF to use the extra ram for malloc. If you're using Arduino then you'll probably need
to use ps_malloc if you have a WROVER module. I've also not been very careful in my memory
usage - so, running this code on a WROOM is probably possible, but you may have to be a bit
more careful and stick to fairly small ILDA files. Anyway, thanks for watching. I'm working on another project
using the laser projector which I'm pretty excited about don't forget to
subscribe and I'll see you in the next video!
GitHub repo is here: https://github.com/atomic14/esp32-laser-show
And the schematics are here: https://oshwlab.com/chris_9044/laser-show-driver-breadboard
Awesome project!
Next up: Vectrex emulator
that is sick!
So impressed.
I definitely have some knowledge gaps in a few things you go over in the video, but damn this is inspiring to close those gaps. Thank you!
Amazing. How much was the custom PCB from JLCPCB?
Wow! Amazing!
THATS CRAZY