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