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!