book collections email follower instructable user
IMG_6788c.JPG
handprint.gif

Ordinary digital cameras work by using a large array of light sensors to capture light as it is reflected from an object. In this experiment, I wanted to see whether I could build a backwards camera: instead of having an array of light sensors, I have just a single sensor; but I control each of 1,024 individual light sources in a 32 x 32 LED matrix.

The way it works is that the Arduino illuminates one LED at a time, while using the analog input to monitor changes in the light sensor. This allows the Arduino to test whether the sensor can "see" a particular LED. This process is repeated for each of the 1,024 individual LEDs rapidly to generate a map of visible pixels.

If an object is placed between the LED matrix and the sensor, the Arduino is able to capture the silhouette of that object, which is lit up as a "shadow" once the capture is complete.

BONUS: With minor tweaks, the same code can be used to implement a "digital stylus" for painting on the LED matrix.

Step 1: Parts Used in This Build

Picture of Parts Used in This Build
IMG_6752c.JPG

For this project, I used the following components:

  • An Arduino Uno with Breadboard
  • 32x32 RGB LED matrix (either from AdaFruit or Tindie)
  • 5V 4A Power Adapter (from AdaFruit)
  • Female DC Power Adapter 2.1mm jack to Screw Terminal block (from AdaFruit)
  • A clear, 3mm TIL78 phototransistor
  • Jumper wires

AdaFruit also sells an Arduino shield which can be used instead of jumper wires.

As I had some Tindie credits, I got my matrix from Tindie, but the matrix from AdaFruit seems to be identical, so either one should work.

The phototransistor came from my decades old collections of parts. It was a clear 3mm part labeled as a TIL78. As far as I can tell, that part is meant for IR and comes either a clear case or a dark casing that blocks visible light. Since the RGB LED matrix puts out visible light, the clear version must be used.

This TIL78 appears to have been discontinued, but I imagine this project could be made using contemporary phototransistors. If you find something that works, let me know and I will update this Instructable!

TitusR420 days ago
Has anyone tried this with a normal display? (e.g: lcd)
random_wang23 days ago
Thank you so much. It gave me some ideas.
eburman1 month ago
O.K. maybe I'm missing something obvious here, but where's the code? I've looked top to bottom several times and there doesn't seem to be any link to the final code. Maybe I'm going nuts. I dunno....
marciot (author)  eburman1 month ago
That's funny, you're the first one to notice! Anyhow, I fixed it now. It's on step 6.
eburman marciot1 month ago
Well I've given it a go but something is wrong. All that I get is a single pixel flashing down at one corner of the matrix panel. There is no light sweep occurring at all. I've been careful to use all of the same parts that you used. I ordered the 32x32 matrix panel through Adafruit. I'm using an authentic Arduino Uno. I'm even using TIL78 phototransistors just like you did that I ordered through eBay and they are working fine. The serial plotter sketch show that my phototransistors are good. All of the RGB matrix panel example sketches are working perfectly as well (the plasma sketch is wild!). But, when I load the BasicMatrixScanner code......nothing. Is there maybe a bug that got into your code? Perhaps you could double check. Thanks!
marciot (author)  eburman1 month ago
It's time to get your troubleshooting hat on and write simple sketches to understand where the problem is. The fact that the Adafruit library works is a good first step. Troubleshooting further will require you to take one step at a time and try a few sample sketches. I'll add a few additional steps to this Instructable with ideas.
marciot (author)  marciot1 month ago
Unfortunately, the Instructables website is broken today and I can't add new steps. But here is some code for you to try (tabs are lost in comments, so this will look messy):

#define CLK 8
#define OE 9
#define LAT 10
#define A A0
#define B A1
#define C A2
#define D A3
#define R1_PIN 2
#define G1_PIN 3
#define B1_PIN 4
#define R2_PIN 5
#define G2_PIN 6
#define B2_PIN 7

void setup() {

// Configure all the matrix control lines as outputs
pinMode(CLK, OUTPUT);
pinMode(LAT, OUTPUT);
pinMode(OE, OUTPUT);
pinMode(A, OUTPUT);
pinMode(B, OUTPUT);
pinMode(C, OUTPUT);
pinMode(D, OUTPUT);
pinMode(R1_PIN, OUTPUT);
pinMode(G1_PIN, OUTPUT);
pinMode(B1_PIN, OUTPUT);
pinMode(R2_PIN, OUTPUT);
pinMode(G2_PIN, OUTPUT);
pinMode(B2_PIN, OUTPUT);

// Enable the output
digitalWrite(OE, LOW);

// Put a RED pixel in the top row, and a green pixel in the BOTTOM row:
digitalWrite(R1_PIN, HIGH);
digitalWrite(G1_PIN, LOW);
digitalWrite(B1_PIN, LOW);
digitalWrite(R2_PIN, LOW);
digitalWrite(G2_PIN, HIGH);
digitalWrite(B2_PIN, LOW);

// Select row zero
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, LOW);

// Clock and latch a row of pixels
for(uint8_t pixels = 0; pixels < 32; pixels++) {
digitalWrite(CLK, HIGH);
delay(10);
digitalWrite(CLK, LOW);
delay(10);
}

// Latch the row to the LED output
digitalWrite(LAT, LOW);
delay(1000);
digitalWrite(LAT, HIGH);
}

void loop() {
}

If this doesn't work, try changing "digitalWrite(OE, LOW)" to "digitalWrite(OE, HIGH)". If that still doesn't help, try exchanging the LOW and HIGH in the "digitalWrite(LAT, LOW)" and "digitalWrite(LAT, HIGH)" lines. Let me know if and whether you get some rows to light up.
eburman marciot1 month ago
I'm not sure what outcome I would be expecting but the unmodified code as written results in two solid lines: Red at row zero and Green at row 16 (if that's the way they're numbered). If I change OE from LOW to HIGH then I get a single very brief flash on the same two corresponding rows. I'm guessing that's the intended result? I also tried the other two possible combinations, one of which again gives me the solid two lines and the other which gives the two briefly flashing rows. Finally I tried swapping in the alternate LED row output latch block and that didn't change the outcomes so it's probably not needed.
marciot (author)  eburman1 month ago
eburman, a red and green line is what I was expecting. This confirms that at least the basics are working. Here is another slightly modified sketch for you to try out. It should cause a single red and green pixel to march across the rows:

#define CLK 8
#define OE 9
#define LAT 10
#define A A0
#define B A1
#define C A2
#define D A3
#define R1_PIN 2
#define G1_PIN 3
#define B1_PIN 4
#define R2_PIN 5
#define G2_PIN 6
#define B2_PIN 7
void setup() {
// Configure all the matrix control lines as outputs
pinMode(CLK, OUTPUT);
pinMode(LAT, OUTPUT);
pinMode(OE, OUTPUT);
pinMode(A, OUTPUT);
pinMode(B, OUTPUT);
pinMode(C, OUTPUT);
pinMode(D, OUTPUT);
pinMode(R1_PIN, OUTPUT);
pinMode(G1_PIN, OUTPUT);
pinMode(B1_PIN, OUTPUT);
pinMode(R2_PIN, OUTPUT);
pinMode(G2_PIN, OUTPUT);
pinMode(B2_PIN, OUTPUT);
// Enable the output
digitalWrite(OE, LOW);
// Put a RED pixel in the top row, and a green pixel in the BOTTOM row:
digitalWrite(R1_PIN, HIGH);
digitalWrite(G1_PIN, LOW);
digitalWrite(B1_PIN, LOW);
digitalWrite(R2_PIN, LOW);
digitalWrite(G2_PIN, HIGH);
digitalWrite(B2_PIN, LOW);
// Select row zero
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, LOW);
// Clock and latch a row of pixels
for(uint8_t pixels = 0; pixels < 32; pixels++) {
digitalWrite(CLK, HIGH);
delay(10);
digitalWrite(CLK, LOW);
delay(10);
// Latch the row to the LED output
digitalWrite(LAT, LOW);
delay(1000);
digitalWrite(LAT, HIGH);
// Turn off the LED input
digitalWrite(R1_PIN, LOW);
digitalWrite(G2_PIN, LOW);
delay(500);
}
// Latch the row to the LED output
digitalWrite(LAT, LOW);
delay(1000);
digitalWrite(LAT, HIGH);
}
void loop() {
}
eburman marciot1 month ago
Bazinga! Yes that is working! I did have to correct a minor error in your code. The final latch block was outside the loop and it wasn't working right so I put it back in. I assume that's what you had intended because the red and green pixels are marching across the rows from one side to the other now. It's interesting to note that if I don't make sure to completely unplug the matrix board from all power sources before loading new code it will behave very erratically. I'm guessing that the shift registers have to be reset to their initial state or they will still be set to whatever state they were in before the new code was run. By the way, thanks for taking time to work through this with me. I'm learning a lot about how shift registers work by going through these exercises.
marciot (author)  eburman1 month ago
That's great! I think I understand why my original code wasn't working now. It has to do with the LAT (latch) input. The LAT signal causes the panel to copy whatever data is in the shift register into the output register that drives the LEDs, but I made an incorrect assumption about how it works. In my code, I leave the latch HIGH while pushing the pixels down the rows -- my panel copies continuously while LAT is held HIGH, but in yours it appears to only happens when LAT *changes* from either LOW to HIGH, or when it changes from HIGH to LOW. I can't tell which direction, so here is some additional code to answer the question:

#define CLK 8
#define OE 9
#define LAT 10
#define A A0
#define B A1
#define C A2
#define D A3
#define R1_PIN 2
#define G1_PIN 3
#define B1_PIN 4
#define R2_PIN 5
#define G2_PIN 6
#define B2_PIN 7
void setup() {
// Configure all the matrix control lines as outputs
pinMode(CLK, OUTPUT);
pinMode(LAT, OUTPUT);
pinMode(OE, OUTPUT);
pinMode(A, OUTPUT);
pinMode(B, OUTPUT);
pinMode(C, OUTPUT);
pinMode(D, OUTPUT);
pinMode(R1_PIN, OUTPUT);
pinMode(G1_PIN, OUTPUT);
pinMode(B1_PIN, OUTPUT);
pinMode(R2_PIN, OUTPUT);
pinMode(G2_PIN, OUTPUT);
pinMode(B2_PIN, OUTPUT);
// Enable the output
digitalWrite(OE, LOW);
// Start all colors off
digitalWrite(R1_PIN, LOW);
digitalWrite(G1_PIN, LOW);
digitalWrite(B1_PIN, LOW);
digitalWrite(R2_PIN, LOW);
digitalWrite(G2_PIN, LOW);
digitalWrite(B2_PIN, LOW);
// Select row zero
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, LOW);
// Start with LAT low
digitalWrite(LAT, LOW);
// Load and shift in red pixels
digitalWrite(R1_PIN, HIGH);
digitalWrite(G1_PIN, LOW);
digitalWrite(B1_PIN, LOW);
digitalWrite(R2_PIN, HIGH);
digitalWrite(G2_PIN, LOW);
digitalWrite(B2_PIN, LOW);
digitalWrite(CLK, HIGH);
digitalWrite(CLK, LOW);
// Trigger LOW to HIGH transition on LAT
digitalWrite(LAT, HIGH);
// Load and shift in green pixels
digitalWrite(R1_PIN, LOW);
digitalWrite(G1_PIN, GREEN);
digitalWrite(B1_PIN, LOW);
digitalWrite(R2_PIN, LOW);
digitalWrite(G2_PIN, GREEN);
digitalWrite(B2_PIN, LOW);
digitalWrite(CLK, HIGH);
digitalWrite(CLK, LOW);
// Trigger HIGH to LOW transition on LAT
digitalWrite(LAT, LOW);
}
void loop() {
}


You should either see red pixels, or red *and* green pixels, after this code runs. Once I know the exact behavior of the LAT I should be able to modify my original scanning code so it works with your panel.

Incidentally, I provided a link to the Adafruit panel because I figured most people would want to buy everything from one place, but my panel was originally from Tindie. My guess is that these panels are built slightly different and my code just made assumptions that weren't valid for the Adafruit panel. It's a good thing you and I are able to work this out and hopefully get the code to works no matter what panel is used! I think we are close!
eburman marciot1 month ago
And the result is: green pixel and red pixel (column 31 green, column 30 red) on row zero. Same again on row 16. Also, corrected GREEN to HIGH. I usually order inexpensive Chinese parts through eBay or Banggood or Aliexpress. This time I ordered the panel through Adafruit. Much more expensive that way but I know that they offer consistantly high quality parts. Since this was a more complex project I wanted to eliminate any questions about the part being defective or incompatible. I guess it didn't work out that way though. But I I'm learning more this way.
marciot (author)  eburman1 month ago
Ha, ha. Sorry, I keep making mistakes in the code I send you. Good thing you're picking up on it enough to correct those :) Anyhow, the results show that your matrix copies the values when LAT goes from HIGH to LOW, which actually is fairly typical for logic circuits, so it does not surprise me that much. I went ahead and modified BasicMatrixScanner.ino with that knowledge. Download the new one and see if it works now!

Anyhow, I'm glad we ended up with different matrices, as otherwise I never would have learned the "proper" way to drive these! Thanks for your patience in sorting this through!
eburman marciot1 month ago
Ta Da! You did it! It seems to be working now just as originally intended. Strong work!
marciot (author)  eburman1 month ago
Glad to hear! Thanks for the help in making the code better!
the idea is brilliant, but now someone needs to put this concept into something that blows everyone's mind!
marciot (author)  BenjaminD1101 month ago
These panels are tillable and meant for large video walls. If someone had a few grand to burn, they could make something large enough to fill a stage and capture an entire band as they played. That would certainly be impressive!
OK now you blew my mind.
jmacuk1 month ago
This looks very cool! Anyone want to try making a simulated Chladni Plate with this?
marciot (author)  jmacuk1 month ago
The plasma demo is pretty cool, but the patterns are not symmetrical as they would be in a Chladni plate (BTW, I had to look that up!)
jmacuk marciot1 month ago
You learn something new every day : )
Parduzco1 month ago
That's pretty neat, and actually very similar to the way an Electron Beam Microscope works. Instead of scanning an array of light sources, and electron beam is scanned in 2D, and the a single detector records the intensity of the reflected signal at each point to reconstruct an image.
marciot (author)  Parduzco1 month ago
Very interesting! Fun fact to know!
gberl0011 month ago
Really neat idea, the LEDs are triggered so fast you can't even tell they're firing one by one (although that may be exacerbated by the refresh rate of your camera). At first glance it looks more like you had an animated bar to clear the screen.

I worked on a project where I created an array of emitters and detectors to make a crude scanner to read surface information, unfortunately this wouldn't exactly work for that project but still a really cool idea.
marciot (author)  gberl0011 month ago
Yes, in person it looks like a quick sweep, sort of like an old radar screen. It would make a good prop for a biometric hand-scanner in a film, I suppose.
OscarC241 month ago
very nice approach, at first i think you where using the fact that led can also be used as very poor photocell and scan each led voltage against the shadow of an object placed in front of it. iam sure it can be done to.
marciot (author)  OscarC241 month ago
Possibly, but with an LED you would need an amplifier of some sort. The Arduino barely had enough resolution in the analogRead for the TIL78, not to mention an LED that wasn't designed for it.
ericCycles1 month ago
LEDs can be used as light sensors, so if your matrix let you measure the voltage across each led before applying power to it, you might be able dispense with the external sensor. But your method is probably more practical :-).
marciot (author)  ericCycles1 month ago
Unfortunately, the RGB matrix already has drivers and a shift-register built-in, so there is no access to the individual LEDs to measure voltage. But if it were just a matrix with no electronics, it might work!
This looks like so much fun! What a clever idea :D
marciot (author)  Penolopy Bulnick1 month ago
Glad you liked it!