Создание самодельных аксессуаров для Nintendo Wii

Я являюсь владельцем Nintendo Wii, и недавно мне стало интересно, как же работает «expansion port» на виимоуте, который создан для подключения различных аксессуаров. При этом я не мог не попытаться создать своё собственное устройство для подключения к Wii.

Предыстория

Началось всё с того, что в так называемом «звёздном каталоге» Nintendo я увидел это:

Это контроллер для Wii в виде геймпада от SNES. Для тех, кто не в курсе, расскажу, что на Wii можно официально покупать и играть в игры от NES (у нас больше известна как «Денди»), SNES (aka Super Nintendo), Nintendo 64 и других классических консолей. Чтобы полноценно играть в такие игры, нужно дополнительно покупать «классический контроллер», который представляет из себя обычный геймпад.

Так вот, этот геймпад в виде оригинального контроллера SNES можно «купить» в звёздном каталоге только за звёздочки, получаемые при покупке игр. Проще говоря, его нельзя так просто купить в магазине как обычный классический геймпад, из-за этого эта штука весьма ценится в узких кругах.

Мне же в голову сразу пришло — почему бы не сделать переходник, который позволял бы подключать к Wiimote (именно к нему подключается классический контроллер, а не напрямую к консоли) настоящий геймпад от SNES, а заодно от NES, Nintendo 64 и чего-нибудь ещё. Так встал вопрос о том, как же устроен порт расширения виимоута.

Теория

После небольшого гугления выяснилось, что там обычный протокол I²C! В разъёме используется пять контактов: земля, питание, clock, data и определение подключения (туда надо просто подать питание).

image

Подключаемое устройство должно иметь адрес 0x52. Передаваемые данные зачем-то шифруются. При чём используется 512-битный ключ, который передаёт сам виимоут. Для дешифрации используется простая формула:

C
decrypted_byte = (encrypted_byte ^ table1[address%8]) + table2[address%8]

Зачем там шифрование? Ума не приложу. К счастью, вскоре выяснилось, что есть уже готовая библиотека для создания своих устройств для Wii:
https://code.google.com/p/circle-of-current/wiki/WiiExtensionLibrary

Она реализует работу с I²C и шифрование-дешифрование для микроконтроллеров AVR. Как раз то, что мне нужно! Остаётся только подставить ID устройства и заполнить массив передаваемыми данными. Вот ID некоторых устройств:

(Информация с wiibrew.org)

Меня интересовал Classic Controller. Далее нужно было разобраться, в каком формате посылать данные. А передаются там шесть байт:

image

LX и LY — это левый аналоговый стик (0-63), RX и RY — это правый аналоговый стик (0-31), LT и RT — это аналоговые шифты (0-31), BD{L,R,U,D} — крестовина, B{ZR,ZL,A,B,X,Y,+,H,-,LT,RT} — кнопки, где 1 — отжата, а 0 — нажата.

Всё несколько запутанно… Ещё стоит брать во внимание, что при включении происходит калибровка устройства и первое полученное значение с аналогов считается центральным.

Реализация

Таким образом, в спокойном положении геймпада массив из шести байт данных будет выглядеть так:

C
unsigned char but_dat[6]; // struct containing button data
but_dat[0] = 0b01011111;  // RX<4:3>	LX<5:0>
but_dat[1] = 0b11011111;  // RX<2:1>	LY<5:0>
but_dat[2] = 0b10001111;  // RX<0>	LT<4:3>	RY<4:0>
but_dat[3] = 0b00000000;  // LT<2:0>	RT<4:0>
but_dat[4] = 0b11111111;  // BDR	BDD	BLT	B-	BH	B+	BRT	 1
but_dat[5] = 0b11111111;  // BZL	BB	BY	BA	BX	BZR	BDL	BDU

Старшие биты у стиков равны нулю, всё остальное равно единице. Так будут передаваться данные, в которых стики находятся по центру, и все кнопки отжаты.

Остаётся только изменять эти значения на необходимые и посылать их, когда виимоут опрашивает наше устройство. Первым делом я решил попробовать подключить игровой контроллер от Денди. Т.е. читать нажимаемые на нём кнопки и посылать их в виимоут, имитирую нажатии на классическом контроллере.

Не буду рассказывать о том, как читать данные с геймпада от Денди, эта тема очень избитая, и делается это очень легко. Можете почитать, например, эту статью: https://habrahabr.ru/post/191936/ авторства . Впрочем, приведу тут код для этого, он совсем небольшой:

C
uint8_t get_nes_gamepad()
{
	uint8_t gamepad_data = 0;
	NES_PORT &= ~(1<<LATCH_PIN); // Latch
	int b;
	for (b = 0; b < 8; b++)
	{
		NES_PORT &= ~(1<<CLOCK_PIN); // Clock
		_delay_us(10);
		gamepad_data |= (((NES_PORT_PIN>>DATA_PIN)&1)<<b);
		NES_PORT |= 1<<CLOCK_PIN; // Clock
		_delay_us(10);
	}		
	NES_PORT |= 1<<LATCH_PIN; // Latch
	_delay_us(10);
	return gamepad_data;
}

Всё заработало без каких-либо задержек и проблем, если не считать, что дендивские контроллеры время не пощадило, пришлось активно оттирать контакты спиртом. Кстати, как ни странно, им хватает питания в 3.3 вольта, хотя везде пишут, что они питаются от пяти вольт. Я уже морально готовился делать конвертер уровней, но не пришлось.

Теперь я могу играть на Wii в игры от NES на контроллере от Денди. Или на родном контроллере от NES, если достану таковой. Впрочем, они отличаются только наличием читерских кнопок турбо.

В планах:

  • Сделать поддержку контроллеров SNES и N64
  • Вытравить плату и оформить всё в красивом корпусе

Исходный код проекта: https://github.com/ClusterM/nes2wii
Основной источник информации: https://wiibrew.org/wiki/Wiimote/Extension_Controllers

Комментарии

Добавить комментарий