[SOLVED]Help! Program hangs at endTransmission when initializing Wii Controller

Hello everyone. I'm new to arduino. Currently I'm trying to make something using Wii Classic Controller as input device. Some days ago I bought a cheap knock-off Wii Classic Controller Pro from the internet, only cost me less than $6. I then googled some information, and wrote the following code:

WiiClassicController.cpp

#include "WiiClassicController.h"


void WiiClassicControllerClass::init(){
	Serial.begin(19200);

	Wire.begin();

	pinMode(18, INPUT);	// Disable internal pull-ups, use external pull-ups(4.7K resistors) instead
	digitalWrite(18, LOW);
	pinMode(19, INPUT);
	digitalWrite(19, LOW);

	TWBR = 12;	// Set I2C speed (12 = 400kHz, 32 = 200kHz, 72 = 100kHz, 152 = 50kHz. 25K or 12.5K or Lower freq needs extra settings)

	delay(100);	// Delay a short period to make PINs stable

	Wire.beginTransmission(WII_CLASSIC_CONTROLLER_7BIT_ADDR);	// The Address of WiiClassicController is 0x52
#ifdef USE_COMPATIBLE_METHOD
	Wire.write(0xF0);
	Wire.write(0x55);
	Wire.endTransmission();	// Start the real transmitting
	Wire.beginTransmission(WII_CLASSIC_CONTROLLER_7BIT_ADDR);	// Another initialization procedure
	Wire.write(0xFB);
	Wire.write(0x00);
#else
	Wire.write(0x40);
	Wire.write(0x00);
#endif
	Wire.endTransmission();	// Stop transmitting

	delay(2);	// Delay a short period to make updates stable

#ifdef CHECK_CONTROLLER_ID
	Wire.beginTransmission(WII_CLASSIC_CONTROLLER_7BIT_ADDR);
	Wire.write(0xFA);	// Check the ID of the controller
	Wire.endTransmission();
	Wire.requestFrom(WII_CLASSIC_CONTROLLER_7BIT_ADDR, 6);
	Serial.print("ID=");
	while(Wire.available()){
		Serial.print(Wire.read(), HEX);
		Serial.print(" ");
	}
	Serial.println();

	delay(2);
#endif
}

void WiiClassicControllerClass::update(){
	Wire.beginTransmission(WII_CLASSIC_CONTROLLER_7BIT_ADDR);
	Wire.write(0x00);
	Wire.endTransmission();

	delay(2);	// 1ms is enough not to overload the controller. This value can be adjusted later to gain a best performance

	Wire.requestFrom(WII_CLASSIC_CONTROLLER_7BIT_ADDR, 6);
	uint8_t t = 0;
	while(Wire.available()){
		rawData[t] = decode(Wire.read());
		t++;
	}
}

void WiiClassicControllerClass::read(NormalizedWiiData* wiiData){
	wiiData->xLAnalog = (rawData[0] & 0x3F);
	wiiData->yLAnalog = (rawData[1] & 0x3F);
	
	wiiData->xRAnalog = ((rawData[0] >> 6) & 0x03) << 3;
	wiiData->xRAnalog |= ((rawData[1] >> 6) & 0x03) << 1;
	wiiData->xRAnalog |= ((rawData[2] >> 7) & 0x01);
	
	wiiData->yRAnalog = (int8_t)(rawData[2] & 0x1F);
	
	wiiData->LTrigger = ((rawData[2] >> 5) & 0x03) << 3;
	wiiData->LTrigger |= (rawData[3] >> 5) & 0x07;
	
	wiiData->RTrigger = (rawData[3] & 0x1F);
	
	*((uint16_t*)&wiiData->buttons) = ~(rawData[4] | (rawData[5] << 8));
	
	wiiData->xLAnalog <<= 2;
	wiiData->yLAnalog <<= 2;
	wiiData->xRAnalog <<= 3;
	wiiData->yRAnalog <<= 3;
	wiiData->LTrigger <<= 3;
	wiiData->RTrigger <<= 3;
}

uint8_t WiiClassicControllerClass::decode(uint8_t raw){
#ifdef USE_COMPATIBLE_METHOD
	return raw;
#else
	return (x ^ 0x17) + 0x17;
#endif
}

WiiClassicControllerClass WiiClassicController;

WiiClassicController.h

#ifndef _WIICLASSICCONTROLLER_h
#define _WIICLASSICCONTROLLER_h

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <Wire.h>


#ifndef WII_CLASSIC_CONTROLLER_7BIT_ADDR
#define WII_CLASSIC_CONTROLLER_7BIT_ADDR	0x52
#endif
#define USE_COMPATIBLE_METHOD
#define CHECK_CONTROLLER_ID


struct NormalizedWiiButton{
	uint8_t dummy	:1;
	uint8_t r		:1;
	uint8_t plus	:1;
	uint8_t home	:1;
	uint8_t minus	:1;
	uint8_t l		:1;
	uint8_t down	:1;
	uint8_t right	:1;

	uint8_t up		:1;
	uint8_t left	:1;
	uint8_t zr		:1;
	uint8_t x		:1;
	uint8_t a		:1;
	uint8_t y		:1;
	uint8_t b		:1;
	uint8_t zl		:1;	
};

struct NormalizedWiiData{
	int8_t xLAnalog;
	int8_t yLAnalog;
	
	int8_t xRAnalog;
	int8_t yRAnalog;
	
	int8_t LTrigger;
	int8_t RTrigger;
	
	NormalizedWiiButton buttons;
};

class WiiClassicControllerClass{
public:
	void init();
	void update();
	void read(NormalizedWiiData* wiiData); 
protected:

private:
	uint8_t decode(uint8_t raw);

	uint8_t rawData[6];

};

extern WiiClassicControllerClass WiiClassicController;

#endif

When I was trying to initialize the Wii Classic Controller using WiiClassicController.init(); I found nothing printed on the COM Monitor.
I then inserted Serial.println(SOME RANDOM CHARACTERS); in every line in the code to see where the problem happened at.
At last I found the program hangs inside the first call of Wire.endTransmission();.

	Wire.write(0xF0);
	Wire.write(0x55);
	Wire.endTransmission();	// <----Program hangs here!
	Wire.beginTransmission(WII_CLASSIC_CONTROLLER_7BIT_ADDR);	// Another initialization procedure
	Wire.write(0xFB);
	Wire.write(0x00);

I checked the circuit and confirmed nothing was wrong. I'm using Arduino Nano so I connected SDA to Analog PIN4 and SCL to Analog PIN5, both pulled up to +3.3V using two 4.7K resistors. VCC and GND also well connected.

I then got confused. Why the program hangs? Could anyone please give me some hints? Thanks very much!

If you want, you can try my library. It's in here, download the newest one at the bottom.

HazardsMind:
If you want, you can try my library. It's in here, download the newest one at the bottom.

Thank you very much for your library! However it is still not working on my circuit... Your code also hangs at the same place.

		Wire.begin();
		Wire.beginTransmission(0x52);      // transmit to device (byte)0x52
		Wire.write(0x40);           // writes memory address
		Wire.write(0x00);           // writes memory address
		Wire.endTransmission();    // <----Hangs Here!
		counter = 0;
		count = 0;

I assume maybe it is a hardware problem?

I well I can assure you it's not the code, so it could either be like you said a hardware issue or wiring issue. Could you post a picture of how it's wired?

OK. I'll take a picture this evening.

There is one more thing I should metion: I also tried using this I2CScanner: Arduino as I2C bus scanner – todbot blog to test my Classic Controller yesterday evening. It printed "I2CScanner ready!" and "Starting scanning of I2C bus from 1 to 100...", and then produced nothing on the serial monitor. I believe this program also hangs at the Wire.endTransmission();, just like your and my code.

What arduino software version are you using?

Post any pictures and information you have and I or someone else will look it over. I am going to bed, it's 1:30 am for me and I have work in the morning.

Will talk then.

You mean the Arduino IDE, right? IDE Version is Arduino 1.0.5-r2. But I'm using Visual Studio 2012 with newest Visual Micro as IDE (which does not affect compiling or uploading or anything related to program executing AFAIK).

Hardware is Arduino Nano with Atmega328P. Like this one: http://arduino.cc/en/Main/ArduinoBoardNano

See: Gammon Forum : Electronics : Microprocessors : I2C - Two-Wire Peripheral Interface - for Arduino

In that post:

Some people have reported on the Arduino forum that I2C "hangs" under certain circumstances. This may well be because the inbuilt "Wire" library expects certain interrupts to happen, and loops until they do. If the interrupt is lost, the library hangs.

An alternative library is described here:
...

I'm not sure if that alternative library is the same one that HazardsMind mentions. In any case it sounds like an electrical problem. Do you have pull-up resistors and if so, what value?

Thank you for your valuable information, but I've fully read the article you posted before I wired my circuit and coded my program. I've had pull-up resistors installed on SDA and SCL. Both are 4.7K, as recommended in the article.

So this is the circuit. I cut off the Controller's connector and soldered it to a tiny board so that it can be easily connected to the breadboard.

IMG_0899[1].JPG

Haruka:
Thank you for your valuable information, but I've fully read the article you posted before I wired my circuit and coded my program.

Did you try the alternative library?

Not yet. I'm going to try it this evening.

Problem solved!
It is indeed the hardware issue. As an I2C device, the cable of the Controller is too long(about 1 m).
I cut the cable off, soldered the nano directly to the back of the Controller's mainboard using some very short wires, and everything worked like a charm.