Arduino Tutorial: Get Traveled Distance using ADNS-9800 Laser Mouse Sensor

Picture of Arduino Tutorial: Get Traveled Distance using ADNS-9800 Laser Mouse Sensor

The ADNS-9800 Laser gaming sensor comprises of sensor and VCSEL in a single chip-on-board (COB) package. ADNS-9800 provides enhanced features like programmable frame rate, programmable resolution, configurable sleep and wake up time to suit various PC gamers’ preferences. The advanced class of VCSEL was engineered by PixArt Imaging to provide a laser diode with a single longitudinal and a single transverse mode. This Laser gaming sensor is in 16-pin integrated chip-on-board (COB) package. It is designed to be used with ADNS-6190-002 small form factor (SFF) gaming laser lens to achieve the optimum performance featured in this document. These parts provide a complete and compact navigation system without moving part and laser calibration process is NOT required in the complete mouse form, thus facilitating high volume assembly. The sensor is based on Laser technology, which measures changes in position by optically acquiring sequential surface images (frames) and mathematically determining the direction and magnitude of movement. It contains an Image Acquisition System (IAS), a Digital Signal Processor (DSP), and a four wire serial port. The IAS acquires microscopic surface images via the lens and illumination system. These images are processed by the DSP to determine the direction and distance of motion. The DSP calculates the Δx and Δy relative displacement values. An external microcontroller reads the Δx and Δy information from the sensor serial port. The microcontroller then translates the data into PS2, USB, or RF signals before sending them to the host PC or game console.

In this tutorial You will learn how to connect ADNS-9800 Laser Mouse Sensor to Arduino and Display Traveled Distance (Δx) on 20x4 LCD

You can buy one from there:

Remove these adsRemove these ads by Signing Up

Step 1: Building Circuit

First You Need to Build Circuit
You have to Connect Your Arduino to ADNS-9800 and LCD Screen
You can download Circuit from there:
1-40 of 74Next »
JasonC111 month ago

Hi Gigi - I'm trying to use this to monitor the speed of movement. At first blush everything seems to be working, but xydat[0] seems to always be -1, 0 or 1 instead of a byte showing the distance moved since the last reading so it's really inaccurate unless you're moving really slowly. Any idea what could be causing this?

wgdal JasonC111 month ago

Hi GIgi, I am having a similar issue. The counter counts up and down, but is very inaccurate at any speed, say 5'/min. Is there a way to change the resolution of the ADNS-9800?


JasonC11 wgdal1 month ago

You can change the resolution using REG_Configuration_I ( page 29). However, this doesn't appear to solve this issue.

wgdal JasonC111 month ago

Hi Jason, any luck making an accurate measurement?

JasonC11 wgdal1 month ago

Hi wgdal - I got it working using Gigi's fix. Where xydat is defined, change it to:

volatile byte xydat[4];

int16_t * x = (int16_t *) &xydat[0];

int16_t * y = (int16_t *) &xydat[2];

And where xydat is read in UpdatePointers(), change to:

xydat[0] = (byte)adns_read_reg(REG_Delta_X_L);

xydat[1] = (byte)adns_read_reg(REG_Delta_X_H);

xydat[2] = (byte)adns_read_reg(REG_Delta_Y_L);

xydat[3] = (byte)adns_read_reg(REG_Delta_Y_H);

Then (*x) and (*y) will contain accurate counts. Make sure you normalize by the CPI to get the distance.

wgdal JasonC1128 days ago

JasonC11, can you please post your code? I applied Gigi's fix, but still no luck. It appeared to change nothing.

Thanks in advance

JasonC11 wgdal21 days ago

wgdal JasonC1121 days ago

Thanks,, but I can't get your code to work either. It doesn't appear to initialize the ADNS. It only returns 0s on the monitor. Thanks

neaton14 wgdal21 days ago

Make sure the "ncs" pin number matches with your arduino/sensor setup.

Pardon my ignorance, but does this mean that the code needs to be adjusted to ensure that it's reading from the correct NCS Pin # based upon where you connected the device physically?


That's exactly right. I think the "ncs pin" is the SS pin on the ADNS-9800. In this example he connects it to pin 10 on the arduino. If you connected it to, say, pin 6 on the arduino, make sure to change the number in code to 6.

Cool beans, thanks for the very useful/valuable information. Will be sure to double check that when I get my setup re-connected.

wgdal neaton1416 days ago

Thanks, That was it. Actually all I need is two output pulses derived from the laser mouse forward and reverse movement, that emulates a rotary encoder. I have a scalable totalizing counter that accepts square wave pulses. I do not need the "y" output. Sounds easy, but I haven't figured it out yet. I am completely new to Arduino and C. Any help would be greatly appreciated.

wgdal, I'm curious if you've come across something like this

Around post #6/#7 they have code examples.

Hey JasonC11, can you explain the math you're doing on the xydat[0] and xydat[2] in this line? I see the 2.54 so I assume you're going from inches to cm, but I'm not sure where the 200 is coming from along with the FS*1e-6, or 0.1 (1e5*1e-6).

distancex = float(*x)/200*2.54/(FS*1e-6);

Any help would be greatly appreciated. Thanks

He neaton14,

The 200 comes from the CPI, which is set at line 177. Thus you get the distance count (*x) divided by the CPI to get inches, multiplied by 2.54 to get cm, and divided by (FS*1e-6) to get cm/second.

Check out p29 of the datasheet ( for more information on how to set the CPI.

I'm not really sure why I set it to 200, but you should be able to tweak it for your application.

Thanks for the explanation! Yeah I tweaked the values through trial and error and now I get solid inch & centimeter readings.

wgdal JasonC1128 days ago

Is this correct?

byte initComplete=0;
byte testctr=0;
unsigned long currTime;
unsigned long timer;
volatile byte xydat[4];
int16_t * x = (int16_t *) &xydat[0];
int16_t * y = (int16_t *) &xydat[2];
volatile byte movementflag=0;
const int ncs = 10;

void UpdatePointer(void){

xydat[0] = (byte)adns_read_reg(REG_Delta_X_L);
xydat[1] = (byte)adns_read_reg(REG_Delta_X_H);
xydat[2] = (byte)adns_read_reg(REG_Delta_Y_L);
xydat[3] = (byte)adns_read_reg(REG_Delta_Y_H);



JasonC11 wgdal27 days ago

Hey wgdal,

I'm out of town until Monday but I'll post it then! If I had to guess, the problem lies in that if you're tracking speed you should change the trigger based on the movement flag to a frame based sampling. I did write this already and will post it on Monday.

~ Jason

Gigi Butbaia (author)  wgdal1 month ago

xydat[0] (the x part of movement) and xydat[1] (the y part of movement) will always show -1 0 or 1. so in order to measure traveled distance using arduino, in loop() I have added variable which value increases by register output, while the register shows non-zero value. Also in my code I only use DELTA_X_LOW and DELTA_Y_LOW registers, to get upper 8 bits of movement you can read DELTA_X_HIGH and DELTA_Y_HIGH. after LOW registers, more info is shown on datasheet pages 24-25.

Is this the loop you are referring to?

int tdistance = 0;
void loop() {
tdistance = tdistance + convTwosComp(xydat[0]);
Serial.println("Distance = " + String(tdistance));

neaton1421 days ago

Hey Gigi. I've been working on getting the ADNS-9800 up and running with the help of your guide & code. The part I'm having trouble with is its accuracy similarly to JasonC11 and wgdal. I have it attached to the bottom of an RC Car and want to be able to track the total distance in X and Y it has traveled relative to its starting position (0,0).

Maybe I just don't understand how the CPI (counts per inch) work. With the resolution set to the lowest (50 CPI), I would expect there to be 50 measurements, or interrupts by the sensor, if I were to move the laser one inch in one direction, giving a tdistance_x = 50 for example.. I'm not getting anywhere near that.

Also if I just tap on the ground near the sensor, the values will jump around as if the sensor thinks its moving.

Has anyone thought of some nifty math to better use the measurements?

DavidCYLi1 month ago

Hi Gigi, I'm trying to understand your original Laser_Mouse arduino code.

I understand that the void loop() function only does something if the movementflag is set to 1. Further above this code we see that this flag changes to 1 in UpdatePointer. The UpdatePointer function is an interrupt service routine. What triggers this pin? (Makes it "Fall"). Does the ADNS board do this periodically? Or something else?


I am curious. Your diagram shows a rotary encoder and was wondering what component you chose for this. Does it work as a scrollwheel or more as a scrollring?

Thanks for the circuit diagram. Extremely helpful. Other instructables have missed this.

Gigi Butbaia (author)  1nDestructable1 month ago

Actually its not rotary encoder, it's ADNS-9800 laser sensor, which don't uses any moving parts like scroll wheel, instead it uses IR laser and optical sensor.

you can also make traveled distance sensor using scrollwheel but circuit and code would be completely different, also it would be less accurate

I'm referring to the component in the bottom right corner that has 3 prongs.

Actually. N/m I see it is a potentiometer.

wgdal1 month ago
Is the laser visible?
wgdal wgdal1 month ago

Thanks again!

Gigi Butbaia (author)  wgdal1 month ago

no it's not visible, because it's IR laser (it's wavelength is ~830 nm, when human eye can see light with wavelength ranging from 390 to 700 nm (visible spectrum), (but you can see it using mobile phone camera))

How do you see it with a camera phone?

wgdal1 month ago

I have assembled, disassembled, and reassembled. I still can't get it to work. I have a Sunfounder uno. It has both a 3.3 volt and 5 volt terminal. All I get is an intermittent Zero displayed in the upper left position.

The serial out displays

Uploading firmware...

Optical Chip Initialized






















Gigi Butbaia (author)  wgdal1 month ago

Have you activated +5V mode?.

To activate +5V mode you have to cut the three traces on the 3.3V side between the exposed sets of pads, and add three solder bridges to the 5V side of the ADNS-9800 board (Be careful don't damage board).

(NOTE: it doesn't matter if you will use +3.3v pin of your microcontroller as VI input, I/O pins still output +5V)

wgdal1 month ago
Is the laser visible?
wgdal1 month ago

What does this mean?

This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.
Arduino: 1.0.6 (Windows 7), Board: "Arduino Uno"
Laser_Mouse.cpp.o: In function `adns_upload_firmware()':
C:\Program Files (x86)\Arduino/Laser_Mouse.ino:147: undefined reference to `firmware_length'
C:\Program Files (x86)\Arduino/Laser_Mouse.ino:147: undefined reference to `firmware_length'
C:\Program Files (x86)\Arduino/Laser_Mouse.ino:147: undefined reference to `firmware_data'
C:\Program Files (x86)\Arduino/Laser_Mouse.ino:147: undefined reference to `firmware_data'


Gigi Butbaia (author)  wgdal1 month ago

looks like you are compiling just Laser_Mouse.ino without firmware,
have you extracted all files from rar to one folder named Laser_Mouse ?

hengl3 months ago

Hi Gigi,

I have acquired one of this sensor and currently playing around with it.

I was wondering if there is any method to use this sensor to determine the distance traveled in cm or meters?



q8naser926 months ago

i have tried using the above connection and code but without lcd and potientimeter by displaying the distance to the serial only and it haven't worked it download no problem but it doesn't specify the distance it just initialize and does nothing please help??

Gigi Butbaia (author)  q8naser926 months ago

Are you using same ADNS-9800 that is in video? if yes then have you activated 5V mode?, have you checked your wiring? can you give me your code? mostly it doesn't initialize because of problems with uploading firmware or connectiong problems with ADNS-9800

I'm experiencing the same problem too. It seems that the firmware is not uploaded properly. Can you please help?

1-40 of 74Next »