Introduction: 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:

Step 1: Building Circuit

Picture of 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:

Step 2: Uploading Code

Picture of Uploading Code

Now Connect Your Arduino to Computer.
Download Arduino Project at
Then Open it, Select Your Board and Your COM Port and Click Upload Button

NOTE: You have to activate +5V mode first (this tutorial is written for arduino uno (any arduino will work which uses Atmega8 - Atmega16 - Atmega32)), 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 board (Be careful don't damage board).When you will activate +5V mode and then connect it correctly it will work fine, but remember code that is uploaded doesn't show distance in meters and you have to decrease loop delay to speedup sensor. If you are removing LCD please edit code to display distance that sensor has traveled


SandraW115 (author)2017-07-11


I'm working on this code now for days but I cant get it to work properly. I want to see my traveled distance in cm in the serial monitor. Is there anyone who got it all working and wants to share his or her conde with me?

I saw a code down here but since it was posted 3 years ago I can't open it anymore...

It is for a project for my study, but I am not really good at arduino.

I tried my best but are almost giving up...

10247048753049823467394 thanks if you want to help!

JasonC11 (author)2014-10-30

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 (author)JasonC112014-11-10

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 (author)wgdal2014-11-10

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

wgdal (author)JasonC112014-11-12

Hi Jason, any luck making an accurate measurement?

JasonC11 (author)wgdal2014-11-12

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 (author)JasonC112014-11-19

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 (author)wgdal2014-11-26

neaton14 (author)JasonC112014-12-08

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

JasonC11 (author)neaton142014-12-08

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.

MagdaL6 (author)JasonC112015-12-09

Hi, just a question, What is mean with the FS value u are using? I get many Data from my sensor and i really try hardly to get the traveled way out of it...but i can't get it...

neaton14 (author)JasonC112014-12-08

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

gametack (author)neaton142015-03-16

Can you share your code. I want to know how you got the good readings.

wgdal (author)JasonC112014-11-26

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 (author)wgdal2014-11-26

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

1nDestructable (author)neaton142014-12-08

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?


neaton14 (author)1nDestructable2014-12-08

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.

1nDestructable (author)neaton142014-12-08

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

wgdal (author)neaton142014-12-01

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.

1nDestructable (author)wgdal2014-12-08

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

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

wgdal (author)JasonC112014-11-19

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 (author)wgdal2014-11-20

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)wgdal2014-11-10

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.

wgdal (author)Gigi Butbaia2014-11-10

Is this the loop you are referring to?

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

nahidalam (author)JasonC112015-03-10

Hi Jason,

In your code, the x, y values comes negative most of the time. Is it related to the direction? If so then which direction - up/down, clockwise/anti-clockwise. Values are not consistent so I can't seem to find the direction implication.

I need to find the rotation angel.

JasonC11 (author)nahidalam2015-03-17

Hi nahidalam,

The values should not be coming out negative most of the time: they are dependent on the direction but I couldn't tell you which direction without knowing the orientation of your sensor. The first thing I would try is to remove the sensor from what you're measuring and try it on a table.

~ Jason

wxtoh (author)2015-11-18

Hi Gigi,

I tried using your program to get the distance travelled, and I have some troubles that I hope that you could help me with. I am using an Arduino Mega 2560, and have converted the sensor to 5V mode. Unfortunately, it has been working rather inconsistently in the sense that it does not return any values on the next try. It is unlikely to be any wiring issue since it was working fine for the first time. The sensor only works for half the time and it works again after retrying.

I have also noticed that the ADNS 9800 sensor (the black portion above the chip) gets really hot sometimes.

Could you help me with the above problems? Thank you so much!

nancy0116 (author)2015-04-24

Hi Gigi, sorry to disturb you again. Now i can read Product_ID and many other registers. but new problem emerges: i can't write to some registers(Configuration_I, Configuration_V), but i can write to Configuration_II.. and i can't figure out why...

nancy0116 (author)2015-04-20

hi Gigi, thx for your post. i have read ProductID successfully, which is 0x33. but the InverseProductID is incorrect(should be 0xcc but i got 0xff)..what problem could it be?

Gigi Butbaia (author)nancy01162015-04-20

Hello nancy0116, thank you commenting :),

Is there a problem with only InverseProductID? Does it print data when you move the sensor?

nancy0116 (author)Gigi Butbaia2015-04-20

Thx for your reply:-D. Among all the registers i can only read ProductID right. others are either 0xff or 0xfe. I.haven't programmed that further to read deltaXY, considering only when i load the firmware successfully can i read the result i have problem reading registers. This is really bugging me.

Gigi Butbaia (author)nancy01162015-04-21

Ok, there's are few things that may be causing such result, it's either a problem with the circuit (check wiring) or you haven't activated +5V mode (for instructions for activating +5V mode see the end of the last step)

nancy0116 (author)Gigi Butbaia2015-04-21

Yeah i guess so.. thanks so much:-D

xu jinqi (author)2015-03-15

HI,Gigi ! I am a foreigner.due to the Internet limited of my country ,I cannot download code by the could you put your ADNS9800 code to my Email urgently need the code to help me finish my job. thanks.

nvntkmr (author)2015-01-16

Hi there,

Can any one please explain how to change the mode from 3.3v to 5v.

i mean which three traces i have to cut and solder to 5 v.

tom_oconnell (author)nvntkmr2015-01-20

I drew it on your picture.

nvntkmr (author)tom_oconnell2015-01-20

thank you for your response. i am using this one in 3.3v mode.
the code is not working.
please refer the link below.

i got this output. please check attached image.

1nDestructable (author)nvntkmr2015-02-28

Are you using a 3.3v arduino? or a 5v? From my understanding if you're using a 5v Arduino you need to make the appropriate traces.

I'm not sure if you are able to tell what mode it is operating in from the output.

nvntkmr (author)1nDestructable2015-02-28

i used 3.3v of arduino.

anasuino (author)2015-01-25

Hi Gigi,

sorry if I sound stupid, but how did you get the firmware data file. because i am trying to interface with another sensor and I checked the datasheet for it (ADNS-6010) and it has the same required setting during the boot-up. I cant find the this mysterious data firmware file anywhere.

please help me.

1nDestructable (author)anasuino2015-02-28

the firmware can be found in the code download for this instructable.

it is a slightly modified version of the original which can be found by Mr John Kicklighter (I'm assuming you bough the whole lense component the ADNS 9800)

ChristopheB2 (author)2015-02-13


I'm still testing the adns but I got a very weird problem and I haven't figured out what causes it. Only when I put the adns-9800 without the lens on a surface it starts properly. If I add the lens and reset the arduino, I no longer get data from the adns. Also, after reset the Inverse_Product_ID and SROM_Version and Motion field show different information if I start the arduino with or without the lens attached. The strange thing is that if I start the arduino without the lens and after a correct boot add the lens, it still works. Anyone experienced similar problems?

hengl (author)2014-09-16

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?



tom_oconnell (author)hengl2015-01-13

If you haven't already solved this, normalize using the CPI according to JasonC11's comment above.

DavidCYLi (author)2014-11-13

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?


tom_oconnell (author)DavidCYLi2015-01-13

According to the ADNS 9800 datasheet, the MOT pin goes low to signal new data accumulated in the dx and dy registers.

1nDestructable (author)2014-11-08

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.

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.

About This Instructable



Bio: Hello, My Name is Giorgi Butbaia, I'm 17 years old
More by Gigi Butbaia:Making microcontroller artificially intelligent - Neural NetworksArduino Phone BookArduino Tutorial: Get Traveled Distance using ADNS-9800 Laser Mouse Sensor
Add instructable to: