Tuesday, June 3, 2008

Introduction

For the past months I have been teaching myself microcontrollers, just for kicks, and my first real project was hooking up two accelerometers<->microcontroller<->PC through USB and see what kind of games I could do with that. I completed the hardware, but had little time to do any fun games. Probably in the summer.

In related news, my mother is a teacher at a local school and one of her students cannot use the mouse, he has a motor problem that I do not know the name – when he tries to hold something steadily, his hands start shaking and he loses all precision in his movements. Well, my mother had seen my 'accelerometers' working, so she asked me if I could do a special mouse for the kid that didn't require steady control. Well, I've read that there are more appropriate methods for cases like this, such as eye control, but they are expensive and you cannot carry it around from computer to computer. Plus, since my mother was paying the bill, it seemed like a good idea to do something like this from an educational point of view :D .

After I did the setup I described in the first paragraph, I had been reading about some nifty experiments with the nunchuck controller. For 20€ you get an accelerometer, 2 buttons and a joystick – a lot of bang-for-the-buck. Plus it would give a pretty sexy mouse. So when I started thinking about the project, it was quite an easy choice of nunchuck vs discrete accelerometers (this and this for example, if you know any cheap dealers in europe, please tell me). Plus I had found some nice information on the internet, and it seemed it wouldn't be that hard to wire up.

In the next pages I am going to report what I did and how I did, just so that anyone which is seeking information as I did can use it as reference. I will talk about:

  • Where did I learn what I needed to know
  • How I wired the hardware
  • How I made it a HID mouse that works in any computer (used example code from Microchip's PICDEM USB demo board :D)
  • How am I processing the joystick and acceleration data to generate mouse movement
  • Discuss that accelerometers working as inclinometers really suck compared to normal mice.

I would like to meet the kid and make a video of him using the mouse, but I'm currently busy (exams) and don't have much time. If anyone has knowledge/experience about what works better in a situation like his, I would be very very interested to know, please post a comment and I will be extremely grateful.

The Nunchuck Mouse with its guts open.

I am currently building this blog so there are quite a few pages missing. I will probably complete it till Sunday.

References

This setup is composed of two parts: the nunchuck <-> microcontroller interface, and the computer <-> PC interface. I will list some references I used in the course of this project.

Nuchuck I2C interface

[1] http://www.windmeadow.com/node/42?page=1

In this site Chad explains how to hook up the Nunchuck to an Arduino board. He also discusses the protocol and wiring.

[2] http://www.ccsinfo.com/forum/viewtopic.php?t=32753&start=11

Forum post about how to use the nunchuck with a PIC using the I2C libraries of the CCS compiler. Interesting to understand the I2C protocol and write/read addresses. One poster reveals that you can use 3.3V inputs for I2C in certain microcontroller ports.

[3] http://www.wiili.org/index.php/Wiimote/Extension_Controllers/Nunchuk

Discussion of the Nunchuck characteristics, including wiring and data interpretation.

[4] http://www.electro-tech-online.com/robotics-chat/36190-communicating-srf05-using-pic18f452.html

The I2C functions from Microchip's C18 Library weren't working very well, but Pommie's (second post) functions worked perfectly.

Other sites:

http://todbot.com/blog/bionicarduino/

Interesting series of lectures to learn some things about microcontrollers(Arduino) in general and a bit about using the Nunchuck.


PIC18F2550 USB Interface

[5] http://ww1.microchip.com/downloads/en/devicedoc/39632c.pdf

The datasheet.

[6] http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2124&param=en532204&page=wwwFullSpeedUSB

The 'MCHPFSUSB v1.3 USB Framework' provides example code for the HID mouse. It also includes bootloader software/firmware, which is nice.

Some months ago there was a wiki called 'pic18fusb' that had lots of interesting information. It seems to be gone, but I've found some sites with some of that info:

http://www.piccoder.co.uk/content/view/42/26/

http://forum.microchip.com/printable.aspx?m=162505

http://www.theroboticshomepage.com/joomla1/content/view/17/30/


Hardware

The circuit is composed of several parts. It is not very complex, as you can see from the following block diagram:

The nunchuck communicates by I2C with the microcontroller, which in turn processes the data and sends 'mouse data' to the computer by USB. There is one detail, though. The nunchuck works at 3.3V, while USB gives 5V. Some have reported that the nunchuck works correctly when wired with 5V [1], and I've tried and it works, but it will probably reduce the nunchuck's lifespan. So I've inserted a voltage converter between the nunchuck and the microcontroller. The more elegant solution would be to use the microcontroller at 3.3V, but when I built the circuit I did not know if one could do the USB communication at that voltage. The PIC18F2550 datasheet discusses such possibilities around page 165.

The whole circuit is represented in the following schematic:

I will briefly describe each 'subsystem'.

  • Oscillator
    A 20MHz crystal is used as the oscillator, just as in the PICDEM USB demo board. Since we will be using code written for that circuit, we should try to be as compatible as possible. Still, to use other crystal you just need to set the appropriate configuration bits.

  • ICSPIn-Circuit Serial Programming is a useful method to program the microcontroller. You just plug in the programmer in the ICSP header and you can reprogram the chip as needed. In my case I used a PicKit2, but any other programmer that supports ICSP should work. The 1KOhm resistor is used to provide insulation from the rest of the circuit, as recommended in the PicKit2 user guide. If you plan on programming your microcontroller by USB bootloading, the inclusion of a permanent ICSP header is unnecessary. You just have to program the chip using ICSP once (in another circuit, for example) and use the USB bootloader to reprogram it when needed.
  • USBThe USB connection is quite simple. The +5V power is drawn from the USB port. I happened to switch the USB + & - so it wasn't working, but if the computer doesn't communicate (start enumeration) with the microcontroller, they are probably switched or the microcontroller is not working well (bad programming?). If the computer tells you that the device failed enumeration, they are well connected but you probably have electromagnetic interference between the wires. When I first built the circuit on a breadboard that was fairly common, in a stripboard it still happened some times, mostly because I was using a small board inserted in a USB extension cord connected to my circuit with long wires. Ugh. It works properly with a savaged USB cord from a el-cheapo USB hub. I could also have cut the extension cord in half, but I'll delay that until the next project. The 220 nF capacitor is needed for the internal 3.3V regulator in the microcontroller, used to drive the USB transciever. The datasheet indicates that the regulator's power is not enough to drive anything other that the transciever and pullups, so we can't use it in the nunchuck. The datasheet advises 220 nF, but I think I am using a 100 nF capacitor and it works.
  • Bootloading Using the appropriate firmware found in the 'MCHPFSUSB v1.3 USB Framework' we can do USB bootloading. USB bootloading consists in programming the microcontroller directly by the USB connection. You still need to program the chip once with ICSP, to load the 'Host' program that is capable of rewriting the code of the 'main' program in the microcontroller. To use the utility provided in the USB Framework by microchip, you need two pushbuttons: on in the reset pin, and the other in the RB4 pin. They work as pullups that short to ground when the button is down. If you reset the microcontroller normally, the bootloaded jumps to the main program and it all works normally. If you reset the microcontroller while holding down the S2 pushbutton, the bootloader will not jump to the main program but will instead start the microcontroller in 'programming' mode, and you will be able to program it from the computer.
  • Voltage Regulator As said, the nunchuck's circuits are designed for 3.3V, but, for example, the LIS3L accelerometer datasheet specifies the absolute maximum voltage as 6V and it works with 5V power, but it shouldn't be very good for the nunchuck's health. So I used a 3.3V regulator between the nunchuck and the microcontroller. I wanted to use a 3.3V Regulator, but my local store did not have those, so I used and adjustable voltage regulator LM117 with the appropriate resistors.
  • Potentiometer We want to change the mouse speed as needed. The easiest method to do it (that I know of) would be to read an analog voltage between 5V-0V in the AN8 port, controlled by a potentiometer. That's it.
  • Connecting to the Nunchuck - Using the resulting 4 wires – 3.3V, Clock, Data, GND - we only need to connect them to the nunchuck. In most of the sites I've read in the internet people have cut the cord, except for this one that created some nice connectors. I started by connecting wires directly to the nunchuck port and it worked well. In the final circuit, due to space constraints, I decided to cut the cord, but any of the two methods work. For the connector 'pinout', check this site and if you cut the cord check this one.

Cost

Parts

Cost

PIC18F2550

8 €

USB Cable

~ 1 € (salvaged)

Voltage Regulator

1 €

2x Button

1 €

Resistances, Capacitors, Wires, Stripboard, Headers

~ 2 €

20MHz Cristal

1 €

Plastic Box

1.5 €

Nunchuck

20€

Potentiometer

1.5€



Total

37€


And that's it. Here is how my circuit looks like assembled in a stripboard:

  1. The microcontroller, with the ICSP header and buttons removed:

  2. The Voltage Regulator with connection to the nunchuck:


Software

The software side can be decomposed into 3 'subsystems':

1) USB HID mouse code

As said, example code from the PICDEM USB demo board will be used. The project we will use is in the directory 'MCHPFSUSB\fw\Hid\Mouse'. It has various files, implementing the USB and HID protocol. The 'user\user_mouse.c' file is where our project's specific code will be. This project was originally written with a PIC18F4550 in mind, so one must make some minor changes to the code:

  • 'io_cfg.h' – since the 2550 doesn't have a D port, remove all references to the D port in the LED definitions (or replace with B)

  • 'autofiles/usbcfg.h' – due to differences between our board and the PICDEM USB board, we must comment these 2 lines: #if defined(PIC18F4550_PICDEM_FS_USB)

#define USE_SELF_POWER_SENSE_IO
#define USE_USB_BUS_SENSE_IO

to

//#define USE_SELF_POWER_SENSE_IO
//#define USE_USB_BUS_SENSE_IO

  • 'rm18f4550.lkr' – change 'FILES p18f4550.lib' to 'FILES p18f2550.lib'

  • In MPLAB, Configure->Select Device , change to 18f2550.

Programming the PIC18F2550 with this project should create a USB HID mouse that moves the pointer in a circle. If not there is something wrong! To diagnose USB connection problems I've used two programs: SnoopyPro and USBVIEW . With USBVIEW you can check if the device failed enumeration or was recognized successfully. With SnoopyPro you can log the traffic between the device and the computer – very nice!

Now one is able to send mouse events to the computer, by controlling the buffer array in the Emulate_Mouse() function.

2) Nunchuck I2C Communication Code

For some reason I wasn't being able to get the C18 library I2C functions to work with the nunchuck. Luckily, I've found some routines in a forum (written by pommie[4]) that worked perfectly. The communication with the nunchuck works as follows:

    • MCU sends nunchuck address (0x52) + write bit (1) = 0xA5

    • MCU writes 0 (8 bits)

    • MCU stops communication

    • Wait a bit for nunchuck to get data ready

    • MCU sends nunchuck address (0x52) + read bit (0) = 0xA4

    • (MCU reads byte + sends ACK ) 5 times

    • MCU reads byte, sends NACK

    • MCU stops communication


And there you go. 6 bytes of data with button presses, accelerometer status and joystick status. The meaning of each byte is explained in a nice table in [3].

3) Generating mouse events with the Nunchuck Data

The buffer array in 'Emulate_Mouse()' has the following structure:

- 1st byte: first bit for left click, second bit for right click, remaining bits are padding

- 2nd and 3rd bytes: X and Y desired dislocation of the cursor.

The whole work goes in the Emulate_Mouse() function.

Buttons

The two buttons on the nunchuck are good candidates for left and right mouse buttons. The C button (bigger) will be the left, and the Z the right. The code to check for button presses is the following:

nunchuck_getdata(array);
buffer[1] = buffer[2] = buffer[0] = 0;

//buttons
if((array[5] & 0b11) == 0b01)
{
buffer[0] = 2;
dirty = 2; //to keep it sending signal, even 0, to release the buttons
}
if((array[5] & 0b11) == 0b0)
{
buffer[0] = 1;
dirty = 2;
}

Where array if a 6 byte array and dirty is a decreasing counter that makes the MCU send mouse signals to the computer even when the buttons are not pressed. The problem is that without it, when we released the button the MCU wouldn't send any message to the computer saying that the button was released. The nunchuck 5th byte is a bit strange: when the Z button is down, it's first 2 bits are 0x01, when C is down, it's 0x00! Testing for that we can fill the buffer[0] variable with the appropriate values.

Potentiometer

We have a potentiometer in the circuit to control the mouse movement speed. Reading it is simple: just read the ADC in port AN8. Initialization:

OpenADC( ADC_FOSC_32 & ADC_RIGHT_JUST & ADC_12_TAD, ADC_CH8 & ADC_REF_VDD_VSS & ADC_INT_OFF, 6 );
SetChanADC(ADC_CH8);

Reading the value:

ConvertADC(); // Start conversion
while( BusyADC() ); // Wait for ADC conversion
adc = ReadADC(); // Read 10bits

Calculating the speed:

delta = 1+(adc >> 7);
if(delta == 8)
delta = 14;
if(delta == 7)
delta = 10;
if(delta == 6)
delta = 8;

The ifs are to induce a little nonlinearity, so that we can reach higher velocities than our linear scale allows.

Joystick

The nunchuck's joystick is easy to control, and while it lacks precision it is not that bad as a mouse.

I used a threshold approach, so that small vibrations of the joystick produce no effect. The code:

if(array[0] > MAXTH)
buffer[1] = delta+delayAdd;
if(array[0] <> MAXTH)
buffer[2] = -delta-delayAdd;

delayAdd is a variable that is incremented while there is movement, to create a feeling of acceleration. MAXTH & MINTH are constants chosen by experimentation. (The full code is attached to the post)

Accelerometer

I've used a similar threshold approach for the accelerometer values:

if(ax > ThAccX)
buffer[1] = delta;
if(ax < -ThAccX) buffer[1] = -delta; if(ay > ThAccY)
buffer[2] = -delta;
if(ay < -ThAccY) buffer[2] = delta;

Again ThAccX,ThAccY were chosen by experimentation.

Sending the Data

if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || dirty > 0)
{
dirty--;
HIDTxReport(buffer,3);
}

If there is data to send or release events, send. Simple.



So that's it. This post covers the main ideas behind the code inside the mouse.

The full MPLAB workspace is here.

Conclusion

I've made a youtube video of the mouse:


As you can see, normal mouses are much better for normal usage. Maybe another GUI paradigm would fit like a glove to an accelerometer based mouse. Wii's games certainly do :)

Theoretically, one could calculate the 2D position of the mouse with the XY acceleration signals, given that they are perpendicular to the gravity axis. The problem is that current MEMS accelerometers, well, suck, i. e., they have significant noise and drift. To calculate position, you need to integrate the signal (twice) , and integrating a very small drift leads to bad results. Plus that mouse wouldn't work in inclined surfaces.

With a 3-axis accelerometer + a gyroscope it would be possible to workout a nice 3D motion sensor (IMU, kalman filter). If you know where to get a cheap gyroscope please comment :)

My mother told me that the kid didn't get along very well with the mouse. He was very impatient and got annoyed at every mistake. Maybe after some training he can get better at it. I've recently found this, and I'm going to suggest it, at least he can use it without special equipment.

I hope this information was usefull to anyone. If you have any questions, fell free to ask through the comments or francisco.silva@REMOVETHISalunos.fc.up.pt