Friday, December 8, 2017

Lightmouse

While surfing over the internet I stumbled upon the Leap Motion Controller and I was flabbergasted. It was something that that really amazed me. Controlling your computer with your hands instead of a mouse. I wanted one.

Then I started thinking. Wouldn't it be possible to make something like this by myself. Well not actually tracking the movement of your fingers. That would be to complicated. But tracking the movement of a hand should be possible.

The general idea was that when your hand moves over a surface the place where the hand is, is in the shade and the rest would be in the light. And that is something you can easily measure by using LDR's (Light Dependend Resistors). So I made a simple setup.



I used two small breadboards next to eachother. This makes it possible to put the LDR's on the breadboard at a certain distance. I attached the LDR's to the analog ports of the Arduino Pro Micro.

Mind you: for this project you really need an Arduino Pro Micro as this has a real USB interface and not a USB-serial interface. We will be needing the USB interface for simulating a mouse.

The LDR's have a pull down resistor for more accurate measuring. To calculate which value the pull-down resistor needs in your particular case use the formula described in this article.
If you do not use the right pull-down resistors the setup will not function  as intended and react poorly at your movements.
In my setup I am using 10K resistors because my setup is intended to work in full light. However if you are using this in more ambient circumstances make sure you alter the values of the resistors using the described formula in the formentioned article.

The first tests were done by just reading the values the LDR's would give when I moved my hand above it. And the results were very satisfying.

Now I had to find the right formula for making something usefull out of this.

Let us look at the following simplified setup.





The square represents the total setup and the black dots represent the LDR's.

If I want to go left my hand has to cover LDR A and C.
For going right it has to cover B and D
Up means covering A and B
Going down can be achieved by covering C and D

By covering the individual leds I indicate that I want to go diagonal.

This can be easily represented in a formula.
Think about this.
If I cover LDR A and C they will get less light as LDR B and D.

X direction =  (A+C) - (B +D)

If the outcome is negative I am moving left. If the outcome is positive I am moving right. Remember that the LDR's give a higher value where there is more light.

Y direction = (A+B) - (C+D)

Same drill as the X direction.

The individual leds are even easier.

If the Value of LDR A drops then I am covering the LDR and want to move UP-LEFT.

And this is really all that is to it.

The only thing I need to test is wether I am actually moving my hand above the setup.
That can easily be done by the following test:

  x = ((A+C) -(B+D));
  y = ((A+B )- (C+D));
  axy = (abs(abs(x)-abs(y)));
 
  if ((abs(x)>25) && (abs(y)>25))

Look at this formula. It just tests if the difference between X and Y is bigger as 25 and that indicates that something is going on above the LDR's. I have substituted the formula in my program by using the variables s1 to s3 which represent the analog lines A0 to A3.

Now we only have to tell the Arduino Pro Micro that it has to act as a mouse by the following command:

void setup()
{
  Mouse.begin();
}

And here we get at a more difficult part of the program. Look at the following lines:

  if ((x<0) && ((abs(x))>(abs(y))) && (y>0) && (axy>100))
    {
      //going left
      Mouse.move(-10, 0, 0);
    }


Let's examine it in detail.

 if ((x<0) && ((abs(x))>(abs(y))) && (y>0)

The first part just tests if  X = smaller as 0. If that is true then this means you want to move left.
As a safety measure I also test if the X value is really bigger as the Y value. That indicates that there is indeed less light on the X LDR's.

&& (axy>100))

This last part tests wether there is a significant difference between the X and Y values. That is to make sure it is my hand moving above the LDR's and not just some shade of a cloud passing by.

Just one last thing to do:

Mouse.move(-10, 0, 0);

And that is why you need to use the Arduino Pro Micro.
This command actually moves the mousepointer of your PC (or Raspberry) to the left by 10 pixels. The 0 following the -10 makes sure that the mouse stays on the same line (Y-coordinate( and just the X moves. The second 0 indicates that the scroll wheel of the mouse should do nothing.

So here is the full listing:



/*Light Mouse
  Luc Volders

Decides on which way the mouse will go by checking
which LDR's get light on and which do not

using 4 LDR's put in a square form
top:    A1, A3
bottom: A0, A2
*/


int s1, s2, s3, s4, x ,y, axy;


void setup()
{
  Mouse.begin();
}

void loop() {
  
  s1 = analogRead(A0);
  s2 = analogRead(A1);
  s3 = analogRead(A2);
  s4 = analogRead(A3);

  x=((s3+s4) -(s1+s2));
  y=((s1+s3)-(s4+s2));
  axy = (abs(abs(x)-abs(y)));
  
  if ((abs(x)>25) && (abs(y)>25)) // test for action
  {
  if ((x<0) && ((abs(x))>(abs(y))) && (y>0) && (axy>100))
    {
      //going left
      Mouse.move(-10, 0, 0);
    }
  if (((x>0) && (y>0)) && (axy>100))
    {
      //going right
      Mouse.move(10, 0, 0);
    }
  if (x<0 && y<0 && axy>100)
    {
      //going up
      Mouse.move(0, -10, 0);
    }
  if (x<0 && (abs(x)<abs(y))&& y>0 && axy>100)
    {  
      //going down
       Mouse.move(0, 10, 0);
    }
  // -------------------------------------------------------
  // schuin
  // -------------------------------------------------------
    if (((x<0) && (y<0)) && (axy<100))
    {
     // moving left-up
      Mouse.move(-10, -10, 0);
    }
    if (((x>0) && (y<0)) && (axy<100))
    {
      //moving right-up
      Mouse.move(10, -10, 0);
    }
    if (((x<0) && (y>0)) && (axy<100))
    {
      //moving left-down
      Mouse.move(-10, 10, 0);
    }
    if (((x>0) && (y>0)) && (axy<100))
    {
      //moving right-down
      Mouse.move(10, 10, 0);
    }
  }
  
  delay(50);
}


After I was sure everything worked fine on the breadboard setup I made an experimental setup using a cardboard.


And after thorough testing I sat behind my computer and designed a casing. This was done in Tinkercad. This design proves that Tinkercad is quite capable.








And this is how it looks in real life.



And man does it work !!!!



I am sorry the video is of such a bad quality but it is the best I could do in my crammed man/cave. I had to make the video using one hand and using the other hand to operate the device.

It also works on my Raspberry Pi and using a USB/OTG adapter it also works on an Android device. I tried it on my Phone and on a tablet with great results.

A lot of possibilities come to my mind for this device. Using it as a joystick for games is obvious. How about adapting the hardware and making a remote for a model boat or car. Gesture controlling a Power-point presentation is another option. Use your imagination.

You can as always find all files on my Github repositry.
https://github.com/Lucvolders/Light-Mouse

So try this for yourselves and have fun.
Till next time

Luc Volders

Friday, December 1, 2017

Analog Pull-Down resistor

We all realise the importance of the pull-up resistor on a digital input pin.
Without a pull-up resistor the initial state of a digital pin is floating and we never will get an accurate reading.

However I never realised how important the pull-up resistor is in an analog circuit. And now I understand it fully I can give you an excellent simple example that demonstrates how valuable such a pull-up resistor is.

Normally an Arduino or an ESP-8266 can read it's analog input and gives it a value between 0 and 1023.
Lets see what happens when we do not use a pull-down resistor and attach an LDR directly to the analog input. In this example I am using an ESP-8266.


Look at the breadboard. The LDR is on one side connected to 3.3 Volts and the other side directly to the analog input of the NodeMCU.






timer 100,[test]

wprint |<h1 style="text-align:center;">Luc Volders</br>Light Tester</br>|
wprint "<br/>"
textbox value
wprint "<br/><br/>"
button "<h2>Off</h2>", [Off]
wprint "<br/>"
wait

[test]
sensor =  io(ai)

value = sensor
wait

[Off]
end


So I wrote a small BASIC program that just reads the analog input port and displays it on a web-page.




When fully exposed to the light the analog reading was 958. When fully covered the reading was 984. Well it kind of works however there is not a lot margin for error. So this is not very usable.

I started searching the net for an LDR setup and indeed there were many examples. They all used a 10K pull-down resistor.



I altered my breadboard setup and attached a 10K pull-down resistor. And indeed it worked.








The value I measured when the LDR was in full light was 527 and when covered with my finger I measured 933. Now this is a much wider range so there is a wider range measurable when the LDR is just partly covered. However it was still not using the full range.


AXEL BENZ FORMULA




Then I found the  Axel-Benz formula.
The Axel-Benz formula is honorably named after one of the first teachers of Physical Computing at the FH Postdam.

How does this formula actually works out.

The formula R-ref = SQR (R-min * R-max) tells us that the value of the of the pull-down resistor should be the Square Root of the (minimal resistance * maximum resistance).

Lets start with measuring the minimal and maximum resistance. We'll do that using our trusted multimeter.





As you can see the value I measured when the LDR was exposed to full light was 420 Ohm. When I covered the LDR with my finger I measured a value of 18.500 Ohm. Now I would have expected a much higher resistance when the LDR was covered so there might be some light leaking around my finger.

Now let us use these figures in the formula.
The formula is: R-REF = SQR (MINIMAL * MAXIMAL)

Lets substitute our figures into the formula:
R-REF = SQR (420 * 18500)
R-REF = SQR (7770000)
R-REF = 2787

This gives us a resistor vaule of 2787 OHM. The nearest by existing value that we can use is a resistor of 2.7K


Therefore I substituted the 10K resistor on the breadboard to a 2.7K version.








And the result is fantastic. The measured values now vary between 257 and 847. This a much broader range in which we can measure more accurately the amount of light that is exposed onto the LDR.

Arduino test


int sensorPin = A0;   
int sensorValue = 0;

void setup() 
{
  Serial.begin(9600);
}

void loop() 
{
  sensorValue = analogRead(sensorPin);
  delay(sensorValue);
  Serial.println(sensorValue);
}

The above experiment is done with an ESP-8266. But how would it work on an Arduino. Well here is the code I used for measuring the Analog port.


I measured the value on port A0.



And here you can see that the values I achieved are almost the same as the ESP gave.

Is everybody Stark Raving Mad then ???

Why on earth is everyone using a 10K resistor then. Has the world gone mad. Well actually yes it has. Just look around you. however not concerning this  particular case.

When you have a full bright light the LDR will have a value of about 0 to 10 OHM. In absolute adrkness it will be around 10000000 (10 meg). Now if we use these values in the formula it will work out as follows:

R-REF = SQR (10 * 10000000)
R-REF = SQR (100000000)
R-REF = 10000

And there we have the 10K value.

However as we saw in the beginning of this story this value did not work out well in this particular case.

So the lesson learned is to always calculate the best value for the project you have at hand.

Till next time
Have fun

Luc Volders