====== Get data from Optical Sensor SPCP168A and display in LEGO NXT ====== ===== 1, Optical Sensor Mouse SPCP168A ===== ==== Description ==== The SPCP168A sensor SoC is a low-cost single-chip optical mouse. The general solution used to implement a non-mechanical tracking engine for computer mice. It is based on SPCP138A optical navigation technology which measures changes in position by optically acquiring sequential surface images and mathematically determining the direction and magnitude of movement. The General optical mouse SoC provides a complete and compact mouse solution. There are no moving parts, and precision optical alignment is not required, few outside components use and facilitate high volume assembly. {{:wiki:mouses.jpg?nolink|}} Structure of optical mouse sensor. The SPCP168A is in an SFF (Small form factor) symmetrical PDIP14-pin optical package and comes with multiple CPI(counts per inch) resolution by CPI button switching and the speed of motion up to 25 inches per second. It includes 3 generally buttons (R、M、L); X-Y motion and a mechanical wheel encoding (1:2)for vertical scrolling and 2 extra 4th / 5th buttons optional. USB MCU inside so that it’s no more mouse controller is needed to interface through USB. The SPCP168A can receive USB command and echo status or data format, both complete USB spec V2.0 and USB HID spec V1.1 compatibility. It is also a cost-effective solution to support USB Optical Mouse. {{ :wiki:spcp168a-datasheet.gif?nolink |}} ==== Application Circuit ==== {{ :wiki:spcp168a-circuit.gif?nolink |}} The SPCP168A datasheet : http://datasheetcafe.databank.netdna-cdn.com/wp-content/uploads/2015/11/SPCP168A.pdf ===== 2, PS/2 protocol ===== ==== PS/2 connection ==== The PS/2 connection is the IBM standard protocol to communicate keyboard, PC mouse with your computer. This protocol has the responsibility to send the key scan codes that you press to the pc and get some response commands from it. This means that we are dealing with a bi-directional type of protocol as each device (pc/keyboard) sends and receives commands. The PS/2 controller interface consists of the PS/2 clock and the PS/2 data inputs, and two 8-bit data ports. One of the 8-bit data ports is used for sending commands to the PS/2 device (mouse or keyboard), the other is for receiving data from the PS/2 device. There are also control signals provided to indicate the arrival of a new command from the PS/2 device and the status of the command transmission to the PS/2 device. The timing control necessary for the PS/2 communication is handled by the controller. ==== PS/2 mouse data packets==== All data is transmitted one byte at a time and each byte is sent in a frame consisting of 11 bits. These bits are: * 1 start bit * 8 data bits * 1 parity bit * 1 stop bit {{:wiki:waveform1.jpg?nolink|}} The mouse keeps track of movement around the X-Y space and the state of its buttons by use of counters. The content of these counters is periodically sent to the host in the form a 4-byte movement data packet. The movement counters represent the mouse's offset relative to its position when the previous movement data packet was issued. ------------------------ D7 D6 D5 D4 D3 D2 D1 D0 (The D0 bit (LSB) is sent first) ------------------------ (1) YV XV YS XS 1 M R L (overflow, sign, buttons) (2) X7 X6 X5 X4 X3 X2 X1 X0 (X movement; -128 to +127) (3) Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 (Y movement; -128 to +127) (4) Z7 Z6 Z5 Z4 Z3 Z2 Z1 Z0 (Z movement; -128 to +127) M = Middle Button State (1 = pressed down) L = Left Button State (1 = pressed down) R = Right Button State (1 = pressed down) XS = Direction of X movement (1 = LEFT) YS = Direction of Y movement (1 = UP) XV = Overflow of X movement value (1 = X overflow occured) YV = Overflow of Y movement value (1 = Y overflow occured) X7,...,X0 : X movement; 8-bit 2's-complement signed byte (-128 to +127) Y7,...,Y0 : Y movement; 8-bit 2's-complement signed byte (-128 to +127) Z7,...,Z0 : Mouse wheel movement; 8-bit 2's-complement signed byte (The Z value is forced to a range of -8 to +7) ===== 3, Materials ===== ==== Materials and Sources ==== This tutorial using Arduino UNO to reading the data from optical mouse using SPCP168A chip, then send data to LEGO NXT via I2C protocol. \\ ^Page Name ^Link| | Arduino UNO |https://www.amazon.com/Arduino-Uno-R3-Microcontroller-A000066/dp/B008GRTSV6 | | Optical Mouse |https://www.amazon.com/dp/B005EJH6RW?aaxitk=s2c7FpAQ07U3yAHigWHp1w&pd_rd_i=B005EJH6RW&pf_rd_m=ATVPDKIKX0DER&pf_rd_p=54dc821a-0937-4e6f-9da9-f8dd5443145d&pf_rd_s=desktop-sx-top-slot&pf_rd_t=301&pf_rd_i=optical+mouse+pc+hp&hsa_cr_id=3553717620401&sb-ci-n=productDescription&sb-ci-v=AmazonBasics%203-Button%20USB%20Wired%20Mouse%20(Black) | | Jumper Wires Male to Male | https://www.amazon.com/SIM-NAT-Solderless-Breadboard-Electronic/dp/B0728C8QHN/ref=asc_df_B0728C8QHN/?tag=hyprod-20&linkCode=df0&hvadid=198072808542&hvpos=1o1&hvnetw=g&hvrand=16554249414238209342&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=9030795&hvtargid=pla-350875878903&psc=1| | USB Female Type A 4-Pin DIP | https://www.amazon.com/Uxcell-a13081900ux0112-Female-Socket-Connector/dp/B00H51E7B0/ref=sr_1_3?s=industrial&ie=UTF8&qid=1538509483&sr=1-3&keywords=USB+Female+Type+A+4-Pin+DIP&dpID=517f8KonJkL&preST=_SX342_QL70_&dpSrc=srch | | LEGO Mindstorms NXT Brick | https://www.amazon.com/LEGO-9841-Mindstorms-NXT-Brick/dp/B002P86B0I/ref=sr_1_cc_2?s=aps&ie=UTF8&qid=1538509542&sr=1-2-catcorr&keywords=lego+nxt+brick | | Lego NXT breadboard adapter | https://www.generationrobots.com/en/401251-nxt-breadboard-adapter-dexter-industries.html | | LEGO Mindstorms NXT / EV3 Connector Cables | https://www.amazon.com/LEGO-Mindstorms-NXT-Connector-Cables/dp/B00G1L8J5A | ===== 4, Interface A Sensor Mouse with Arduino via PS/2 protocol ===== ==== Connection ==== {{:wiki:connectmoue.png?nolink|}} {{:wiki:43045370_547672939015809_2107383385193185280_n.jpg?nolink|}} ==== Code ==== PS/2 Write void writeByte(char data) { int parityBit = 1; high(_dataPin);//set pin high high(_clockPin);//set pin high delayMicroseconds(300); low(_clockPin);//set pin low delayMicroseconds(300); low(_dataPin);//set pin low delayMicroseconds(10); // start bit high(_clockPin); waitForClockState(LOW); // data for (int i = 0; i < 8; i++) { int dataBit = bitRead(data, i); writeBit(dataBit); parityBit = parityBit ^ dataBit; } // parity bit writeBit(parityBit); // stop bit high(_dataPin); delayMicroseconds(50); waitForClockState(LOW); // wait for mouse to switch modes while ((digitalRead(_clockPin) == LOW) || (digitalRead(_dataPin) == LOW)) ; // put a hold on the incoming data low(_clockPin); } PS/2 Reading void writeByte(char data) { int parityBit = 1; high(_dataPin);//set pin high high(_clockPin);//set pin high delayMicroseconds(300); low(_clockPin);//set pin low delayMicroseconds(300); low(_dataPin);//set pin low delayMicroseconds(10); // start bit high(_clockPin); waitForClockState(LOW); // data for (int i = 0; i < 8; i++) { int dataBit = bitRead(data, i); writeBit(dataBit); parityBit = parityBit ^ dataBit; } // parity bit writeBit(parityBit); // stop bit high(_dataPin); delayMicroseconds(50); waitForClockState(LOW); // wait for mouse to switch modes while ((digitalRead(_clockPin) == LOW) || (digitalRead(_dataPin) == LOW)) ; // put a hold on the incoming data low(_clockPin); } Reading data from optical mouse sensor void readdd() { requestData(); Dstatu = readByte(); // this byte for buttons Dx = readByte(); Dy = readByte(); if (_supportsIntelliMouseExtensions) { DataWheel[count] = readByte(); count++; } X += (int)Dx; Y += (int)Dy; if(count==3) { for(count;count>=0;count--) { Sum+=(int)DataWheel[count]; } if(Sum>0) W++; else if(Sum<0) W--; Sum=0;count=0; } } I2C event //---------------------------------I2C Events-------------------------------// void receiveEvent(int bytesReceived){ int i=0; clearReceiveBuffer(); while(Wire.available()){ receiveBuffer[i++] = Wire.read(); } } void requestEvent(){ int memoryAddress = receiveBuffer[0]; if (memoryAddress >= ARD_DATA )//&& memoryAddress <=ARD_DATA + NUM_OF_SENSORS) { uint8_t data[8]; // each sensor is 2 byte value char dir=0; if(X<0) {dir|=0x01;xdata=abs(X);}else xdata=X; // dir&0x01==1 is mean X<0 else X>0 if(Y<0) {dir|=0x02;ydata=abs(Y);}else ydata=Y; // dir&0x02==1 is mean Y<0 else Y>0 if(W<0) {dir|=0x04;wdata=abs(W);}else wdata=W; // dir&0x04==1 is mean W<0 else W>0 data[0]=highByte(dir); //data[0] and data[1] to Determine the negative or positive value of X,Y,W. data[1]=lowByte(dir); data[2]=highByte(xdata);//data[2] and data[3] absolute value of X axis data[3]=lowByte(xdata); data[4]=highByte(ydata); data[4] and data[5] absolute value of Y axis data[5]=lowByte(ydata); data[6]=highByte(wdata); data[7]=lowByte(wdata); data[6] and data[7] absolute value of Wheel int startidx = memoryAddress-ARD_DATA; Wire.write(data+startidx,8); //Send to end of buffer } } ===== 5, Send data from an Arduino to LEGO NXT via I2C ===== **Connection** {{:wiki:fullccc.png?nolink|}} {{:wiki:42982714_486068395227202_1657342972317925376_n.jpg?nolink|}} **Code** Read and dislay data #pragma config(Sensor, S1,ard,sensorI2CCustom) #include "common.h" // I2C lib #include "settings.h" ubyte I2CReply[17] ; //technically we should use tByteArray ubyte I2CRequest[17]; int xdata; int ydata; int wdata; int datas; // take two bytes and make an integer int makeInt(ubyte highbyte,ubyte lowbyte){ return (highbyte<<8) | lowbyte; } int makeInt(ubyte highbyte,ubyte lowbyte){ return (highbyte<<8) | lowbyte; } //start at memoryAddress and return back replysize in bytes int I2CArduino(tSensors link, ubyte memoryAddress,ubyte replysize) { memset(I2CRequest, 0, 17); memset(I2CReply, 0, 17); I2CRequest[0] = 2; // Message size I2CRequest[1] = ARDUINO_I2C_ADDR; // I2C Address I2CRequest[2] = memoryAddress; // memory address if (!writeI2C(link,I2CRequest,I2CReply,replysize)){ return -1; } return 0; } //=========================================================== task main () { nI2CRetries = 3; clearDebugStream(); writeDebugStreamLine("Starting..."); wait1Msec(500); while (true){ if (I2CArduino(ard,ARD_DATA,8)){ writeDebugStreamLine("ERROROR!!"); } datas=makeInt(I2CReply[0],I2CReply[1]); // take two bytes for Determine the negative or positive value of X,Y,W. xdata=makeInt(I2CReply[2],I2CReply[3]); // take two bytes for X axis ydata=makeInt(I2CReply[4],I2CReply[5]); // take two bytes for Y axis wdata=makeInt(I2CReply[6],I2CReply[7]); // take two bytes for W axis if(datas&0x01) xdata=-xdata;// if (datas&0x01==1) X<0 if(datas&0x02) ydata=-ydata;// if (datas&0x02==1) Y<0 if(datas&0x04) wdata=-wdata;// if (datas&0x04==1) W<0 nxtDisplayTextLine(1,"(%4d,%4d)",xdata,ydata); nxtDisplayTextLine(3,"%4d",wdata); } } **Video** {{youtube>NjTEpLt_3GU?medium}} ===== Reference ===== - https://en.wikipedia.org/wiki/Optical_mouse - http://pcbheaven.com/wikipages/The_PS2_protocol/ - https://playground.arduino.cc/Main/InterfacingWithHardware#mouse - http://www.networktechinc.com/ps2-prots.html - http://datasheetcafe.databank.netdna-cdn.com/wp-content/uploads/2015/11/SPCP168A.pdf If you have any questions please contact for me via email phamquyenanh.qb@gmail.com. ===== Download ===== {{:wiki:i2cnxtandarduino.rar|}}