High Level Design
The painting is created by a vertical pole with a flashing one-meter LED strip attached to a cart being pushed by two motors. A user can use their DSLR camera to view the long exposure effect. To upload an image, the user specifies a URL through SSH on a Raspberry Pi 0 W which receives and resizes the image. The Raspberry Pi then sends the RGB image data to the PIC 32 in parallel, which is connected to the LED strip and motors. The image content is sent as an 8-bit RGB value per pixel. The PIC 32 converts the pixel data into RGB values for the LED strip and changes the colors at an appropriate speed to create an image while the motors move the entire apparatus horizontally. By moving the strip horizontally, we were able to recreate images of varying widths.
Our mechanical design, parallel communication connection, motor control circuit, LED strip circuit, and our power solution.
We mounted the 144 LED/meter dotStar strip on a 3 foot piece of wood. The LED stick was mounted onto a cardboard box that contained the Raspberry Pi, PIC 32, breadboard, and power supplies. The box sat on a wood frame that was connected to the motors and wheels, allowing the mechanism to move in one direction of any distance.
We also added five layers of tissue paper on the LED strip to help diffuse the light and create a more blended image without sacrificing light intensity.
We decided to connect the Raspberry Pi Zero to the PIC 32 through parallel connection with two different enable pins. To accomplish this, we sent an 8-bit RGB value through 8 GPIO ports, with a data enable pin than indicated when the color for each LED was sent, and a power enable pin that indicated when to start and stop the motors. This pin allowed us to start the motors upon data transfer and stop the motors once all the data has been sent and the image has completed. Each of these connections had a 330Ω resistor between the pins to limit current and protect the microcontrollers.
The power enable pin controls when the motors are turned on and off. The two motors are connected in parallel while being isolated from the PIC 32 to protect it from inductive currents. We used the motor control circuit from the ECE 4760 Lab 3 webpage.
The power enable pin controls when the motors are turned on and off. The two motors are connected in parallel while the motors are controlled by a PWM output with a 1 K Hz frequency. With a 40M Hz clock frequency, this means that the PWM output can have a duty-cycle ranging from 0 to 39999. Because DC motors are inductive loads, the release of the stored energy causes harmful voltage spikes that could damage the MCU. To protect the MCU, an optoisolator is used to electrically isolate the motor from the MCU. The optoisolator used is the 4N35. Additionally, a diode and capacitor were placed in parallel to the motor to moderate the motor voltage during the PWM transitions. A MOSFET was used to switch the motor on and off using the PWM signal and resistors (300 Ω, 1M Ω, and 10K Ω) were added to protect and optimize the circuit as shown below.
The DotStar LED strip was connected to the PIC 32 through a 3.3 to 5 V logic shifter, 74LS125, since the LED strip requires 5V data, and the PIC 32 only outputs 3.3V. The connections are shown in figure 5.
The Raspberry Pi, PIC 32, and LED strip were powered at 5V through a USB connected power bank. This was possible by running the LED strip at the lowest intensity, where it would draw less than one amp. The motors were also powered using a separate 5V power bank that was connected through a USB cable.
On the PIC 32, we ran a uni-threaded program. The program contains a few initial setup calls to open timers and declare input pins, before than starting one thread. Although this program could have been made with no threading library, we decided to include the protothreads library due to our familiarity and experience with the library and the negligible disadvantages of unnecessarily using threading in this application.
The program begins by opening Timer 2 with a generate period of 40,000, which corresponds to a frequency of about 1KHz. This timer is used to trigger an ISR which generates the PWM pulses necessary for precise motor control. After opening the timer, the program continues to open Output Compare 3, which is used to send the PWM signal to the motor control circuit. The output compare works off of a pwm_on_time, which corresponds to the amount of time to hold the PWM signal high, translating to a faster motor. The motors should be off initialing, so pwm_on_time is set to 0 at first. After opening the timer and output compare, the last thing to initialize is the SPI channel used to send data to the DotStar LED strip. We chose SPI channel 2 for the LED strip because the onboard TFT display on our PIC32 Big Board uses SPI channel 1, and we wanted to use this display for debugging purposes. After these three modules are open and configured, we are already to start our lone thread.
The thread starts by initializing the red, green, and blue (RGB) values of all pixels to 0, corresponding to a completely black or “off” strip. After this initialization, the thread goes into a while(1) loop where it uses function calls to read RGB data, set RGB data, and write RGB data. The thread accomplishes this through repetitive function calls to three functions: read_pixeldata and set_pixel_rgb, and write_pixels.
A Raspberry Pi 0 W was used to retrieve the images from the internet and parse and format the RGB data to send to the PIC 32 for display. All of the necessary code was condensed into a single .py file that was run wirelessly through SSH. To begin, the user can specify a url of an image and the Pi will download it and rename it to a user-specified file name. Once the file is downloaded on the Pi, it will prompt the user for a filename to display using theDotStar strip. After opening the file, the program uses the Pillow, a fork of the Python Image Library (PIL) to resize the image to fit the 144 pixel height constraint. Then, the program iterates over all the pixels in the image and saves their scaled RGB data. After saving all the RGB data, a starting LED sequence is sent to the PIC to visually indicate to the photographer to start their exposure. Once one column’s worth of data is transferred the data enable pin goes high then low, to signal that the data lines are all valid values for the PIC to read. Similar to on the PIC, once all this data is transmitted, the process has to be repeated for as many columns as the image has, after which the power enable line will be set low and the PIC will stop the motors.
The DotStar Light Painter was able to produce images effectively. However, because we had to reduce the color from 24 total RGB bits to 8 total RGB bits, some color accuracy is lost. Additionally, because we only had 144 LEDs per meter, resolution was also lost (depending upon the original resolution of the image). To increase the resolution, another LED strip could be added. Also, the LED strip could have performed at a higher speed if we had the Raspberry Pi send all the image data at once, however, since the Raspberry Pi sends the data column by column, there is a bit of delay. We did reduce the delay between each column sent from 0.5 seconds to .05 seconds, which shrunk the width of the image.
However, sending data column by column with our slow motors worked to our advantage. The slow speed of the motors was due to too much stress from the torque of the box and strip, so motors with a higher torque tolerance would also have allowed us to better tune the images. Lastly, our mechanical design could have been sturdier. As you can see in the images, the cart rocked side to side as it moved, creating warped images. This could have been prevented through a sturdier design and better materials.
We are very pleased with how our project turned out. We successfully programmed and built a mechanism to draw any image with a LED strip and a long exposure camera. We are pleasantly surprised at the definition of the images we were able to draw with our limited resolution.
Source: DotStar Light Painter