Thursday, 5 May 2016

Scrolling text on guitar fingerboard

This was always going to be an experiment. And one that we're not entirely sure would work. But, in true Nerd Club fashion, it wouldn't be the first time we'd invested a lot of time and effort in trying something out without being sure of success from  the outset. That's probably how all the best stuff gets invented anyway. Probably.

Our light-up guitar firmware is pretty much finished. But we've still got nearly 20% programming space left on the AVR. Which means there's still time to shoe-horn in some unnecessary functionality.



So what about.. scrolling text? The idea with this was to have a scrolling message running from the head, towards the body. The text would be made up of simple bitmap glyphs 6x4 pixels wide (6x5 in the case of the letters M and W).


Each character could be described by 5 bytes but since our fingerboard is 6, not 8, LEDs high, we're only interested in the lower 6 bits of each byte.

The letter A, for example, would be described as (binary)

00011110
00101000
00101000
00011110
00000000

(if you look at this sideways, you should "see" the letter A in the positive bits of the binary values) which in turn is represented by the decimal values 30, 62, 62, 30, 0.

Then a quick bit of VB6 code "read" our font bitmap and produced array values for each letter. The final Arduino code looked a little bit like this:


const unsigned char characters[] PROGMEM =
{
   30,40,40,30,0,
   62,42,42,20,0,
   28,34,34,34,0,
   62,34,34,28,0,
   62,42,42,34,0,
   62,40,40,32,0,
   28,34,42,46,0,
   62,8,8,62,0,
   34,62,34,0,0,
   4,34,34,60,0,
   62,8,20,34,0,
   62,2,2,0,0,
   62,16,12,16,62,
   62,16,8,62,0,
   28,34,34,28,0,
   62,40,40,16,0,
   28,42,38,30,0,
   62,40,40,22,0,
   18,42,42,36,0,
   32,62,32,0,0,
   60,2,2,62,0,
   56,6,4,56,0,
   62,4,24,4,62,
   54,8,8,54,0,
   50,10,10,60,0,
   38,42,42,50,0,


   4,42,42,30,0,
   62,18,18,12,0,
   12,18,18,18,0,
   12,18,18,62,0,
   28,42,42,18,0,
   31,40,40,0,0,
   9,21,21,30,0,
   62,16,16,14,0,
   2,46,2,0,0,
   1,9,46,0,0,
   62,8,8,22,0,
   60,2,2,0,0,
   14,16,12,16,14,
   14,16,16,14,0,
   12,18,18,12,0,
   31,18,18,12,0,
   12,18,18,15,0,
   14,16,16,0,0,
   18,42,42,36,0,
   60,18,18,0,0,
   28,2,2,30,0,
   24,6,4,24,0,
   28,2,12,2,28,
   18,12,12,18,0,
   25,5,5,30,0,
   18,22,26,18,0,
   

   28,34,34,28,0,
   18,62,2,0,0,
   38,42,42,18,0,
   34,42,42,20,0,
   56,8,30,8,0,
   58,42,42,36,0,
   28,42,42,4,0,
   32,38,40,48,0,
   20,42,42,20,0,
   18,42,42,28,0,   
   6,42,46,34,28,
   0,20,0,0,0,
   2,28,32,0,0,
   0,58,58,0,0,
   8,28,28,8,0,
   2,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0,
   0,0,0,0,0
};


Which allowed us to recall and display the appropriate pixels in a recurring function call:

// move all previous "pixels" along to the left
for (int i = 0; i < NUM_LEDS-6; i++) {
     leds[i] = leds[i+6];
}

// now decide which pattern to display on the right-most six pixels
int k=scrollText.length();
if(curr_character < k){
currLetter = scrollText.charAt(curr_character);
}else{
currLetter = " ";
}

// unless we decide otherwise, assume a blank/space character
byte b[2];
currLetter.getBytes(b,2);
int p=b[0];
k=78;

if(isUpperCase(p)){ k=p-65; }
if(isLowerCase(p)){ k=(p-97)+26; }
if(isDigit(p)){ k=(p-48)+52; }

// get the pattern for the current letter
k=(k*5) + col_count;
curr_col_pattern = pgm_read_byte_near(characters + k);

if(curr_col_pattern==0){
     curr_character++;
     col_count=0;
}else{
     col_count++;
     if(col_count>=5){
          curr_character++;
          col_count=0;
     }
}

// now put the current column pattern onto the right-most pixels
k=curr_col_pattern<<1;
k=k<<1;

for(int i=5; i>=0; i--){
     if((k & 128) > 0){
          leds[(NUM_LEDS-(5-i))-1] = scroll_text_colour;
     }else{
          leds[(NUM_LEDS-(5-i))-1] = CRGB::Black;
     }
     k=k<<1;
}

// update the LEDs
FastLED.show();


After a bit of tweaking and debugging, we finally got our text scrolling along the neck.
Here's a (very) quick demo:


it's quite difficult to read, but if you focus on the left-hand edge of the display, you might just make out the shape of the letters  as they whizz by


The only thing is.... it doesn't actually look very good.
Sure, it might look fine on a small, closely or more densely populated LED matrix. But on a guitar fingerboard, where the LEDs are spaced so far apart (particularly higher up the neck) it just looks like a jumbled mess of flashing lights.

So there we go.
Another Nerd Club fail.
Another load of wasted hours on  something that doesn't work.

In fact, that's not strictly true. It does work. Or, at least, it works - functionally - how we expected it to. It's just not really suitable for this particular task. But that doesn't make it a failure. And, indeed, while quite a few hours went into developing, debugging and making it work, they were hardly wasted. We came up with an idea, executed it, and got the LEDs to flash in the sequence we determined at the start. To come up with an idea and bring it to life is never a waste of time. In this case, it wasn't the execution, but the idea that wasn't suitable.

And at the very start, we didn't know if the idea was going to work. So we built something to test the idea. And the thing worked, and proved that the idea wasn't a very good one. So in just about every respect, this one was a resounding success. Yet, somehow, it doesn't quite feel like a success....

Then again, if you're guaranteed success before you start, you're not actually creating anything; you may as well buy and build Ikea furniture and say you made it yourself!

Saturday, 30 April 2016

Guitar fingerboard routing

It's been a while since we got the CNC router up and running. What with real life and work and waiting for some new router bits to arrive, it's been a long time coming. But finally, this weekend we got our fingerboard routed to the point where it's actually usable on a guitar neck. And actually mounted the neck onto a guitar to make an actual, playable instrument.

[photo]

There are still a few "light leaks" but a little bit of work with some fine-tooth files to get the LEDs to fit (where they've been hand-soldered onto the PCB with a little less care than they needed) and we should have a fully sealed, ready-to-play fingerboard

[photo]

So now we're onto designing a two-part PCB for the screen and controller.
We've got a working prototype on breadboard, so hopefully it's a straight-forward translation to get it onto a PCB and embedded inside the guitar body. With



Friday, 29 April 2016

TQFP socket programmer for Atmega328 SMT chips

Coming from an "industrial" rather than a "hobby" background, I always try to incorporate some kind of ICSP header into my (final) product designs. It's just something I've always done.

There's nothing worse that being asked to support a piece of hardware and after a number of years wanting to upgrade the firmware and having to desolder microcontrollers off their controller boards; who knows what damage you do when taking them off - and can you be sure they go back on and are fully working?

If you de-solder the chip, change the firmware and reinstall it and it doesn't work, how do you trace the fault? It could be the new firmware doesn't work as expected - but it might be that you're a genius coder, got everything right first time, and just haven't quite soldered all the pins back correctly, or have a dry joint or something.

It's the main difference I see between "hobbyists" and "commercial" developers.
A hobby developer will "work forwards" - find the things that make something work and follow the route to a conclusion. Usually, a "commercial" developer (and particularly one with lots of experience of the "forward working" method) will "work backwards".

That means, start from a point of failure.
Put yourself in the shoes of the poor sap who, in four years time, when the thing has failed or needs upgrading, is standing in front of your hardware, screwdriver in hand, cursing the one who could have made their life so much easier, with just a simple tweak of  the design. That poor sap might even be you one day! Commercial developers tend to start with failure and work back - if this goes wrong, how do I put it right? Before this can happen, what conditions have to be met? If these conditions are not met, should my device even be allowed to run?

And in the "industrial" microcontroller world - where PIC still reigns (or, at least, did up until about ten years ago, when I stopped being seriously involved with industrial electronics and become more of a "hobbyist" myself) that means one of two things:

Either put every chip in a socket.
That's the easiest method.
But also - particularly for a hobbyist trying out ideas - the most expensive, leading to larger, more cumbersome looking designs (and in this time of miniaturisation, smaller is almost always better/more preferable).

The other method is to put an ICSP header on everything.
That's what I normally do.
Any PIC-based design, I'll make sure my programming pins are easily accessible so, should the time every come, it's dead easy to whack on a programmer and update the firmware. And, although ICSP programming isn't quite as common in Arduino-land, it's not unheard of.

Since using Arduino/AVR chips in a few projects this year, I've found the 0.8mm SMT pitch chips are great for hand-soldering (0.5mm pitch PICs on the other hand are a real pain to solder onto home-made PCBs). Since smaller is better, and a few of the other nerds have been pushing for AVR- rather than PIC-based projects, I thought I'd try to leave off the programming header on a few homebrew projects, to get the design-size down as small as possible (quite often my programming header is about the same size as the rest of the entire PCB!)

Which is where little beauty comes in.


It's a 32-pin TQFP socket on a DIP-sized breakout board. We looked at these a few years back, and back then they were £70 or more. If you find them on Farnell, they're still stupid money.
Thankfully, the Chinese pirates are happy to knock them out at less than £15 and sell them on eBay. So it seemed like a no-brainer to get one to try out.

You can either program your AVR chips "properly" using an AVR programmer and Atmel Studio/avrdude. Or you can find the serial and reset pins, and program them using the bootloader over serial.

Since our chips are to be used for "one time" projects, we just burned the firmware directly onto the chip, without using the bootloader. It seemed a bit silly to burn the bootloader using the ICSP pins, then have to use a completely different set of pins to program the firmware, so we just dumped the firmware straight on.

The only thing that wasn't quite so straightforward was location of the pins.
We couldn't just assume that "pin one" on the breakout board (the top-left-most pin) was connected to pin one on the TQFP chip. In fact, it turned out not to be the case at all.


But after a bit of poking around with a multi-meter we managed to map out which of the socket pins went to which of the DIP pins on the breakout board.











Saturday, 23 April 2016

How to avoid setting off scanners at the airport

Last weekend a few of us took to the skies and went to set up an "installation" at Frankfurt. As I was "technical support" my work was done by Tuesday morning, so I flew home alone.

Feeling rather pleased with myself, for know that an airport in Germany is the "flughafen" and that the main train station was the "hauptbanhoff" I felt pretty confident in getting to the airport without any problems. And so it turned out. So problem-free was my return to the airport that I had nearly four hours to kill.

So a couple of hours in, and after reading the papers (I still had Sunday's tomes with me to get through) I opened up the laptop, got a brew and a sticky bun and - thanks to the free wifi - set about a little web development.


After a couple of hours, the battery in the laptop gave up, so I got out the back-up and carried out cranking out some php and javascript on the little notebook.

Unlike British airports, where you go through security and then hang around in duty free, trying to avoid that cloying sickly stench from the perfume counters, at Frankfurt, each departure gate has it's own dedicated security entrance. So it's only once your flight has been assigned a gate, can you pass through security.

So, with just an hour 'til lift-off, the gate opened, and I threw the notebook into my bag and dashed off to get through security. That's where things started to go wrong!

Firstly, I set off the body scanner.
Normally, I do this anyway. At Liverpool, Heathrow and Gatwick, minus shoes, belt, glasses and with empty pockets, I always set the scanner off. To this day, I still don't know why. A quick pat-down and everything is ok. So setting the body-scanner off at Frankfurt didn't worry me.

Except this time, there was a genuine reason.
But it took two goes to find out why - a couple of loose atmega328 surface mount chips had snagged in the lining of a pocket and it took a while to even realise that they were there.

Obviously, this didn't go down too well with the German Authorities. But, having explained that we'd just been at a conference, making and installing a custom electronics-based installation, they seemed to accept this as a reasonable excuse for carrying loose electronics components!

So, once through the scanner, I asked if I could have my rucksack.
It had been held back, as it had set off the heat detector.
A quick rummage through my dirty clothes and they found the offending notebook - still warm from being used just a few minutes earlier. Another, innocent, reason for setting off the detectors!

So I got my notebook back and the rucksack was passed through the scanner again.
And it failed the checks again. After asking if the bottle of water I was carrying could go through security (it couldn't) I'd left the bottle behind. But forgot to take out my toothpaste. And, as we all know by now, fluids on aeroplanes - however viscous - are frowned upon, especially if you say you're not carrying any.

A third check, a third, innocent excuse.
By now, I was starting to look like a hapless first-time traveller.
What started out as a humorous distraction was becoming a bit of an irritation. Unfortunately, the girl on the security desk was starting to think so too.

So I got my bag back, and my notebook, and left my toothpaste at the counter. Some loose electronic parts, undeclared fluids and something glowing white hot on the heat-sensor didn't appear suspicious at all... I put all my stuff back in my pockets, and casually asked if I could also have the laptop back, that they'd taken out of my bag.

And that's when things took a slightly darker turn.
As the girl on the security desk said "this one is not so funny".
With no further explanation, she said that "for this one, I need the police".
And went and got the police.


Not the friendly bobby in a slightly-too-tall hat with a silver knob on the top like we have "back home". This was the German Airport Security police. And not even the friendly-looking airport police with discreet holster guns. She went at got the federal police. Like the army-branch of the police (I'm still not exactly sure how different police forces work in Europe, but apparently there are different "grades" of police - and these were the big, scary, police)  And they wear body armour. And carry machine guns!


(note to self: real machine guns look surprisingly plastic-y in real life, more like a video games controller than an actual killing device!)

When two of these guys turned up, one pushed me in the chest telling me "you don't move" while the other, even more threatening-looking one (note the position of the leading finger in the image, almost on the trigger - this is how they hold their guns) barked something incomprehensible in German. I tried to talk to them via the girl on the security desk (whose English was excellent, but - now that she didn't want to be associated with me - had suddenly become less conversational, and her tone was more similar to the barking instructions coming from the police).

I desperately tried to think why they might confiscate my laptop. I'd been using it almost solidly for the last 36 hours - coding everything for the installation during the day, getting a bite to eat, then coding into the early hours of the morning. It hadn't left my sight (except during mealtimes and sleeping) for the entire time I was in Germany. I knew there was nothing wrong with it. And yet....

The laptop was wiped over, across the screen and keyboard, and the swab placed into a machine. A red light came on. The girl on the desk informed me that I had been singled out for an explosives test. And I'd failed. I didn't feel, at this point - with machine guns trained on me - that I should maybe point out that I was the least explosive person I knew and that the problem had, in fact, been with my laptop.

Then it came to me - a few weeks earlier, Jake at BuildBrighton had smashed open the old laptop battery; the replacement battery, currently in the laptop, causing a potential bomb-scare at Frankfurt airport, had obviously come from a dodgy manufacturer in China, from where I'd bought it on eBay. I tried to explain this to the guards, and as I went to remove the battery from the laptop, was told - rather more firmly this time - that I was not to move, and to touch nothing!

A few phone calls later - initially to "London" (I didn't hear who, but I distinctly heard them say "London") and I was taken away from the main counter, and into a little area around the corner - out of sight of everybody else. It was only really at this point, that something like panic started to set in.

I had my passport taken and every page copied.
Forms with "something something alerte" in block capitals on the top were filled in. My bank card details were taken. I was asked where the laptop was bought, and PC World in Hove were called. This lead to another phone call to somewhere else - who then advised the guards to phone Hewlett Packard (the manufacturers of the laptop). Following this, a call was made to "something something consulate". I was starting to really worry.

While all this was going on, the girl from the security desk took the battery out of the laptop, and took another swab from the screen and keyboard of the now open laptop (minus the potentially explosive battery). The swab went into the little machine and.... mirp, failed.

Once again I was asked where I'd been, what I'd been doing, both before and during my time in Germany. The laptop failed the "explosives test" for a third time. It was only now that I started to realise just how dodgy things looked - some loose electronics components in my pocket, a bag showing up hot in the heat scanner and failing to declare the presence of fluids in my hand-luggage. To put a tin hat on the whole affair, my laptop had now failed the test for the presence of "trace explosives" not just once, but three times!

Now I've learned to love visiting Germany.
And I've learned to love Germans. Their blunt, directness can be both refreshing and funny. After finishing a meal at a restaurant (inbetween setting up the installation and going back to the hotel for yet more coding) a waitress told us one evening "you must now pay the bill and leave" once she realised we'd finished our meal. A directness that we're still not sure if it's due to culture, or a lack of nuance in translating into another language. Germans are - generally - pretty blunt, and very direct. Some people confuse this with being rude, possibly even aggressive (coupled with a language that sounds like they're shouting all the time). I'm sure they're not.

But all this means when they think you're potentially carrying explosive materials, the average German Airport Security guard, comes across as quite angry. And I don't blame them. I'd be angry too if some bastard tried to get explosives onto a 'plane I was responsible for guarding. And I'd certainly give them what-for if I caught them. Which meant, by now - and with just a few minutes until boarding - I was really starting to worry. People were just shouting and pointing guns at me, sounding more and more agitated as time went on. My flight was about to leave, and nobody knew I was in a little room away from the main waiting area. I had no idea what was being said but some pretty important-sounding people were being called.

A few more 'phone calls and another swab test. This time, and with no explanation, the light came up green. A further test also came up "clean". It seems that whatever had set the detector off was no longer present on my keyboard/screen.

So the guards turned on their heels and walked off!
The girl on the desk gave me my laptop back.
And then wished me a "good flight".

And that was that.
I'd gone from terrorist suspect #1 to "Mr Bean on holiday". And they let me get on the aeroplane.

Whether or not it's related, on returning to Heathrow, I couldn't help but notice a lot of armed police around the place. There are usually a couple of guards with guns at the airport. And maybe it's because of my experience that I was looking for them. But there were loads of armed guards around Heathrow on the afternoon I touched down.

To this day I'm not entirely sure what caused the red alert on the explosives sensor. I can appreciate that I didn't help my cause at the beginning, but trace elements of explosives?! On recounting the tale, Nick immediately suggested cinnamon.

Apparently, cinnamon (and, curiously, coriander) dust is highly volatile. It's quite possible that this is what cause the explosive sensor to flag an alert. After all, I'd been munching on a cinnamon bun (and slurping tea) just a few hours before, while using the laptop. Is it vaguely possible that traces of cinnamon were transferred onto the keyboard?

That's the only explanation I can think of. Which is why my advice when flying and to avoid setting the scanners off is

  1. Don't be a pillock and walk through the body scanner with loose electronic components in your pockets
  2. Don't carry hot electronics devices through security
  3. Stay away from cinnamon buns at the airport café!







Wednesday, 13 April 2016

VB6 G-Code editor

After getting over-excited about getting VB6 to run in my Windows 10 laptop, I pulled the trigger and published the last post without actually demonstrating how the app works.

So here it is:


From the Inkscape-generated G-code file, it's now possible to selection a region of the code and "export" it as a section. Before actually running the g-code, the VB6 app adds in the extremities, so when it comes to routing anything, we can double-check that it will fit inside the boundaries of the piece being milled.

The M0 command after each "preview move" halts program execution and allows us to take as long as necessary to check the position of the cutting head. The pause in the video is simply because I had to power up the spindle manually (since it can't yet take spin-start commands from Mach3).

The routed section exactly matches the extreme positions of the shape as plotted at the very beginning. After routing the entire guitar fingboard, we ended up with something like this....

Routing hardwoods doesn't half leave a mess on the cutting bed!

Tuesday, 12 April 2016

Welcome back VB6

It's only been a few weeks since I went nuclear on my laptop with a fresh install of Windows 10 (it behaves far better than it did following the 8-to-8.1-to-10 upgrade path but is still pretty shonky). It took only a day or two to get all my usual development tools on there - including weird stuff like HeidiSQL, WinSCP, NotePad++, WAMP as well as the usual PIC simulators, ExpressPCB and even Arduino.

But for a long while, something was missing.
I've been messing about with Raspberry Pi and Linux and Arduino and even a bit of Python. But it's taken until now to get VB6 installed on my machine. And it was like putting on an old pair of slippers. Sure, there's a hole in the toe and they're not cool enough to be seen in just popping down the road to the shops. But VB6 is an awesome bit of kit for bashing out apps in a couple of hours.

And Microsoft still haven't found the heart to kill it off.
.NET based applications are slow and bloated and buggy.
VB6 exes just work (and have done since the runtimes were included in Windows 2000). They're lightweight (just a few hundred kb) responsive (no watching the screen redraw as often happens with .NET based cack) and easy to deploy/distribute (just copy the .exe onto your machine and you're done - no stupid 1.5Gb framework downloads here!)

In fact, VB6 proved it's worth in just 45 minutes after being installed. That's how long it took to hack together a quick g-code editor which has massively improved the usability of my CNC router, despite it not having any end stops.

My g-code editor app reads a g-code file and lists each line. You can select any line or lines and apply a "global nudge" - increase or decrease any of the values by a specific amount. You can add a bit to the Z depth (to increase a drill depth for example) or just overwrite the minimum and maximum values for the Z-axis entirely.


But where it really shines, is it allows me to select a block of g-code and it will plot a "bounding box" around the points and create a second g-code file I can just load up into Mach3 Mill.
The new g-code file, before carrying out any of the actual instructions, moves to the top-most, left-most, right-most and bottom-most corners of the highlighted section of g-code, stopping at each point.

This gives me time to inspect the position of the cutting head to make sure it's within the boundaries of the piece I'm working on, before committing to the cut.

It's great for making sure I'm not about to plough through the edges of a guitar fingerboard, when routing the channels on the back. Without it, I'd just have to hope that I'd managed to line up my fingerboard absolutely perfectly when placing it on the cutting bed and I'd have to keep my fingers crossed after hitting the "go" button.

This way, I can take each section (that is cut just above each fret on the fingerboard) and make sure that the cutting head stays within the boundaries of the fingerboard. If it doesn't I'll see it and have change to correct it (using the "global nudge" function) without wasting more valuable rosewood!



Monday, 11 April 2016

Creating an IR (infrared) tracking webcam

After we managed to get colour tracking with OpenCV working, we hit a snag during "real world" testing. While it works fine in principle, in a controlled environment, trying to put it to use under normal conditions wasn't quite so easy.

At first, everything worked fine - although we'd hard-coded the colour range to look for, we figured this could easily be made into a parameter. But during the course of the day, our colour detection started to behave differently.

At the start of the day, the detection routine correctly placed the centre-point right in the middle of the object it was tracking. But as the day wore on, the centre-point slowly drifted south! When it became noticeable, we looked at the raw input image and discovered that as the sunlight had shifted, coming into the room, it was casting shadows in different places on our tracking object.

We needed a way of filtering out the shadows.
As we can't control the light conditions of the room in which our object tracking will ultimately be used, it essentially means not using visible light. Which, of course, means IR (infrared).

Cheap webcams are great for tracking infrared light.
Most cameras on mobile phones show infrared light - if you shine a remote control at your iPhone camera, you can see it blinking on and off. But some webcams don't. That's because they're usually fitted with an infrared filter, to try to maintain the right colour balance, using different light sources.

Really cheap webcams simply use a cover over the sensor to take out infrared from any incoming image. It's this idea that we're going to exploit, to make our webcam see infrared.

Here's our really cheap webcam (about £6 off Amazon). We took off the lens to expose the image sensor. Shock horror, no IR filter....


Not to worry - it was fixed over the lens.


Simply removing the IR filter means that our webcam can now see infrared light. But that's only half the story. Seeing previously invisible light means we've actually got more content to track, not less. What we really need, now we can see the invisible IR light, is to remove all the visible light entering the webcam sensor.

You can use polaroid/polarising filters, but just as good (and more easily sourced) is developed camera film. Remember when you used to load a film into your camera, take your snaps, then have them developed at a chemist or camera shop? Well, it's that sort of film.

What we need is just a piece of photography film, fully opaque (black) and developed. The easiest way to achieve this is to get a black-and-white film, pull a length of it out and hold it out in the sunshine for about five minutes. Then take the film to be developed the "old school way".

We had to tell the developing company that we didn't actually want the photos (they'd just be bright white pieces of paper anyway) and to develop whatever was on the film - some chemists might return your film with "error" or some comment about it being faulty. Pre-warning them that the pictures are actually junk means that they don't waste time trying to put right something that isn't there!


Then we simply cut a small square of developed film and used it in place of the IR filter.
The result? Our webcam can see absolutely nothing. An entirely black image appeared on the screen. But when we flashed an IR LED in front of it....



Bingo!