mirror of
https://github.com/demik/quack.git
synced 2024-11-23 12:31:27 +00:00
added wii remote support (buttons)
This commit is contained in:
parent
85fcf88209
commit
2ab47ae4db
@ -4,8 +4,9 @@ set(srcs "adb.c"
|
||||
"gpio.c"
|
||||
"led.c"
|
||||
"main.c"
|
||||
"quad.c")
|
||||
|
||||
"quad.c"
|
||||
"wii.c")
|
||||
|
||||
set(include_dirs ".")
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
|
@ -3,7 +3,7 @@
|
||||
* quack
|
||||
*
|
||||
* Created by Michel DEPEIGE on 7/01/2020.
|
||||
* Copyright (c) 2020 Michel DEPEIGE.
|
||||
* Copyright (c) 2020-2024 Michel DEPEIGE.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the Apache License, Version 2.0 (the "License");
|
||||
|
434
main/blue.c
434
main/blue.c
@ -41,6 +41,7 @@
|
||||
#endif
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt_device.h"
|
||||
#include "esp_gap_bt_api.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
#include "esp_hid_common.h"
|
||||
@ -53,6 +54,7 @@
|
||||
#include "gpio.h"
|
||||
#include "led.h"
|
||||
#include "m4848.h"
|
||||
#include "wii.h"
|
||||
|
||||
/* debug tag */
|
||||
static const char *TAG = "blue";
|
||||
@ -230,158 +232,36 @@ void blue_adb2hid(void *pvParameters) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Host specific functions blue_h_* */
|
||||
void blue_h_callback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data)
|
||||
{
|
||||
esp_hidh_event_t event = (esp_hidh_event_t)id;
|
||||
esp_hidh_event_data_t *param = (esp_hidh_event_data_t *)event_data;
|
||||
|
||||
const uint8_t *bda = NULL;
|
||||
|
||||
/* union described in include/esp_hidh.h */
|
||||
switch (event) {
|
||||
case ESP_HIDH_OPEN_EVENT: {
|
||||
esp_hidh_dev_dump(param->open.dev, stdout);
|
||||
blue_h_open(param);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_BATTERY_EVENT: {
|
||||
bda = esp_hidh_dev_bda_get(param->battery.dev);
|
||||
ESP_LOGI(TAG, ESP_BD_ADDR_STR " battery: %d%%", ESP_BD_ADDR_HEX(bda), param->battery.level);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_INPUT_EVENT: {
|
||||
//ESP_LOG_BUFFER_HEX(TAG, param->input.data, param->input.length);
|
||||
xTaskNotify(t_yellow, LED_ONCE, eSetValueWithOverwrite);
|
||||
blue_h_input(param->input.dev, param->input.data, param->input.length);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_FEATURE_EVENT: {
|
||||
bda = esp_hidh_dev_bda_get(param->feature.dev);
|
||||
ESP_LOGI(TAG, ESP_BD_ADDR_STR " FEATURE: %8s, MAP: %2u, ID: %3u, Len: %d", ESP_BD_ADDR_HEX(bda), esp_hid_usage_str(param->feature.usage), param->feature.map_index, param->feature.report_id, param->feature.length);
|
||||
ESP_LOG_BUFFER_HEX(TAG, param->feature.data, param->feature.length);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_CLOSE_EVENT: {
|
||||
blue_h_close(param);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGI(TAG, "Unknwown event: %d", event);
|
||||
}
|
||||
}
|
||||
|
||||
void blue_h_close(esp_hidh_event_data_t *p) {
|
||||
const uint8_t *bda = NULL;
|
||||
|
||||
configASSERT(p != NULL);
|
||||
bda = esp_hidh_dev_bda_get(p->close.dev);
|
||||
ESP_LOGI(TAG, "closed connection with device " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(bda));
|
||||
esp_hidh_dev_free(p->close.dev);
|
||||
|
||||
blue_pointers--;
|
||||
if (! blue_pointers)
|
||||
xTaskNotify(t_blue, LED_SLOW, eSetValueWithOverwrite);
|
||||
}
|
||||
|
||||
static void blue_handle_button(uint8_t buttons) {
|
||||
static bool locked = false;
|
||||
static bool releasable = true;
|
||||
static bool status = BLUE_BUTTON_N; // Keep state
|
||||
|
||||
buttons = buttons & (BLUE_BUTTON_1 | BLUE_BUTTON_2 | BLUE_BUTTON_3);
|
||||
|
||||
if (status && buttons)
|
||||
return ;
|
||||
if (status == BLUE_BUTTON_N && buttons == BLUE_BUTTON_N && !locked)
|
||||
return ;
|
||||
if (buttons == BLUE_BUTTON_2 && locked && !releasable)
|
||||
return ;
|
||||
if (status == BLUE_BUTTON_N && buttons == BLUE_BUTTON_N && locked) {
|
||||
releasable = true;
|
||||
return ;
|
||||
}
|
||||
|
||||
/* release button */
|
||||
if (status && buttons == BLUE_BUTTON_N && !locked) {
|
||||
xTaskNotify(t_click, 0, eSetValueWithOverwrite);
|
||||
status = 0;
|
||||
ESP_LOGD(TAG, "button released");
|
||||
return ;
|
||||
}
|
||||
|
||||
/* stick button on right click */
|
||||
if (status == BLUE_BUTTON_N && buttons == BLUE_BUTTON_2 && !locked) {
|
||||
xTaskNotify(t_click, 1, eSetValueWithOverwrite);
|
||||
locked = true;
|
||||
releasable = false;
|
||||
ESP_LOGD(TAG, "button locked");
|
||||
return ;
|
||||
}
|
||||
|
||||
/* press button (simple click) */
|
||||
if (status == BLUE_BUTTON_N && buttons) {
|
||||
xTaskNotify(t_click, 1, eSetValueWithOverwrite);
|
||||
locked = false;
|
||||
status = 1;
|
||||
ESP_LOGD(TAG, "button pressed");
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
static void blue_h_init(void) {
|
||||
esp_hidh_config_t config = {
|
||||
.callback = blue_h_callback,
|
||||
.event_stack_size = 4096
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_hidh_init(&config));
|
||||
|
||||
blue_pointers = 0;
|
||||
xTaskCreatePinnedToCore(blue_scan, "blue_scan", 6 * 1024, NULL, 2, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bluetooth common init: init module and various stuff
|
||||
* Host or Device specific inits go in blue_d_init() or blue_h_init()
|
||||
* this function try to guess wich pin code to use when asked for one
|
||||
* it just harass the different drivers and get a pin code from the driver
|
||||
*/
|
||||
void blue_init(void)
|
||||
uint8_t blue_get_pin_code(esp_bd_addr_t bda, esp_bt_pin_code_t pin)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
if (wii_is_nintendo_bda(bda)) {
|
||||
wii_generate_pin(pin);
|
||||
ESP_LOGD(TAG, "Nintendo device asking for pin, trying with pin " WII_PIN_STR,
|
||||
WII_PIN_HEX(pin));
|
||||
return 6;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Starting Bluetooth init on core %d", xPortGetCoreID());
|
||||
ESP_ERROR_CHECK(ret);
|
||||
ESP_ERROR_CHECK(esp_hid_gap_init(ESP_BT_MODE_CLASSIC_BT));
|
||||
/* let's try with default pin */
|
||||
ESP_LOGI(TAG, "no match found, trying with default pin code: 1234");
|
||||
pin[0] = '1';
|
||||
pin[0] = '2';
|
||||
pin[0] = '3';
|
||||
pin[0] = '4';
|
||||
|
||||
esp_log_level_set("event", ESP_LOG_INFO);
|
||||
/* complains about wrong data len on BOOT mode and CCONTROL */
|
||||
esp_log_level_set("BT_HIDH", ESP_LOG_ERROR);
|
||||
|
||||
/*
|
||||
* at this point, everything but bluetooth is started.
|
||||
* put green steady, start blinking blue and keep scanning until a device is found
|
||||
*
|
||||
* starting IDF 5.2.2, sometimes the first xTaskNotify() call may be lost. why ?
|
||||
*/
|
||||
|
||||
xTaskNotify(t_green, LED_ON, eSetValueWithOverwrite);
|
||||
xTaskNotify(t_green, LED_ON, eSetValueWithOverwrite);
|
||||
xTaskNotify(t_blue, LED_FAST, eSetValueWithOverwrite);
|
||||
|
||||
if (adb_is_host())
|
||||
blue_d_init();
|
||||
else
|
||||
blue_h_init();
|
||||
return 4;
|
||||
}
|
||||
|
||||
/************************************
|
||||
* Host specific functions blue_h_* *
|
||||
************************************/
|
||||
|
||||
/*
|
||||
* data follows HID Mouse boot protocol format
|
||||
* this function will decode a boot protocol frame and convert it for quadrature tasks
|
||||
* data arg follows HID Mouse boot protocol format
|
||||
*
|
||||
* Byte | Bits | Description
|
||||
* -----+------+---------------------------------------------------------------
|
||||
@ -394,12 +274,11 @@ void blue_init(void)
|
||||
* 3+ | 0-7 | Device specific, usually 3 is scroll wheel, usually unused (0)
|
||||
*/
|
||||
|
||||
void blue_h_input(esp_hidh_dev_t *dev, uint8_t *data, uint16_t length) {
|
||||
void blue_h_boot(uint8_t *data, uint16_t length) {
|
||||
uint8_t buttons;
|
||||
uint8_t i;
|
||||
int8_t x, y;
|
||||
|
||||
//ESP_LOG_BUFFER_HEX(TAG, data, length);
|
||||
buttons = data[0];
|
||||
|
||||
/*
|
||||
@ -486,19 +365,198 @@ void blue_h_input(esp_hidh_dev_t *dev, uint8_t *data, uint16_t length) {
|
||||
}
|
||||
}
|
||||
|
||||
void blue_h_open(esp_hidh_event_data_t *p) {
|
||||
void blue_h_callback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data)
|
||||
{
|
||||
esp_hidh_event_t event = (esp_hidh_event_t)id;
|
||||
esp_hidh_event_data_t *param = (esp_hidh_event_data_t *)event_data;
|
||||
|
||||
const uint8_t *bda = NULL;
|
||||
|
||||
/* union described in include/esp_hidh.h */
|
||||
switch (event) {
|
||||
case ESP_HIDH_OPEN_EVENT: {
|
||||
esp_hidh_dev_dump(param->open.dev, stdout);
|
||||
blue_h_open(param);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_BATTERY_EVENT: {
|
||||
bda = esp_hidh_dev_bda_get(param->battery.dev);
|
||||
ESP_LOGI(TAG, ESP_BD_ADDR_STR " battery: %d%%", ESP_BD_ADDR_HEX(bda), param->battery.level);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_INPUT_EVENT: {
|
||||
//ESP_LOG_BUFFER_HEX(TAG, param->input.data, param->input.length);
|
||||
xTaskNotify(t_yellow, LED_ONCE, eSetValueWithOverwrite);
|
||||
blue_h_input(param->input.dev, param->input.report_id, param->input.data, param->input.length);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_FEATURE_EVENT: {
|
||||
bda = esp_hidh_dev_bda_get(param->feature.dev);
|
||||
ESP_LOGI(TAG, ESP_BD_ADDR_STR " FEATURE: %8s, MAP: %2u, ID: %3u, Len: %d", ESP_BD_ADDR_HEX(bda), esp_hid_usage_str(param->feature.usage), param->feature.map_index, param->feature.report_id, param->feature.length);
|
||||
ESP_LOG_BUFFER_HEX(TAG, param->feature.data, param->feature.length);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_CLOSE_EVENT: {
|
||||
blue_h_close(param);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGI(TAG, "Unknwown event: %d", event);
|
||||
}
|
||||
}
|
||||
|
||||
void blue_h_close(esp_hidh_event_data_t *p) {
|
||||
const uint8_t *bda = NULL;
|
||||
|
||||
configASSERT(p != NULL);
|
||||
bda = esp_hidh_dev_bda_get(p->close.dev);
|
||||
ESP_LOGI(TAG, "closed connection with device " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(bda));
|
||||
esp_hidh_dev_free(p->close.dev);
|
||||
|
||||
blue_pointers--;
|
||||
if (! blue_pointers)
|
||||
xTaskNotify(t_blue, LED_SLOW, eSetValueWithOverwrite);
|
||||
}
|
||||
|
||||
/*
|
||||
* this function dispatch the input message to the correct driver
|
||||
* the detection is based on Product ID + Vendor ID lists that each driver provides
|
||||
* if no specific driver claims that combinaison, try with the generic boot driver
|
||||
*/
|
||||
void blue_h_input(esp_hidh_dev_t *dev, uint16_t id, uint8_t *data, uint16_t length) {
|
||||
uint16_t vid = esp_hidh_dev_vendor_id_get(dev);
|
||||
uint16_t pid = esp_hidh_dev_product_id_get(dev);
|
||||
|
||||
if (wii_is_wiimote(vid, pid)) {
|
||||
wii_input(dev, id, data, length);
|
||||
return;
|
||||
}
|
||||
|
||||
blue_h_boot(data, length);
|
||||
}
|
||||
|
||||
static void blue_h_init(void) {
|
||||
esp_hidh_config_t config = {
|
||||
.callback = blue_h_callback,
|
||||
.event_stack_size = 4096
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_hidh_init(&config));
|
||||
|
||||
blue_pointers = 0;
|
||||
xTaskCreatePinnedToCore(blue_scan, "blue_scan", 6 * 1024, NULL, 2, NULL, 0);
|
||||
|
||||
/* bluetooth stack ready at this point, initialise wii stuff there */
|
||||
wii_init();
|
||||
}
|
||||
|
||||
static void blue_handle_button(uint8_t buttons) {
|
||||
static bool locked = false;
|
||||
static bool releasable = true;
|
||||
static bool status = BLUE_BUTTON_N; // Keep state
|
||||
|
||||
buttons = buttons & (BLUE_BUTTON_1 | BLUE_BUTTON_2 | BLUE_BUTTON_3);
|
||||
|
||||
if (status && buttons)
|
||||
return ;
|
||||
if (status == BLUE_BUTTON_N && buttons == BLUE_BUTTON_N && !locked)
|
||||
return ;
|
||||
if (buttons == BLUE_BUTTON_2 && locked && !releasable)
|
||||
return ;
|
||||
if (status == BLUE_BUTTON_N && buttons == BLUE_BUTTON_N && locked) {
|
||||
releasable = true;
|
||||
return ;
|
||||
}
|
||||
|
||||
/* release button */
|
||||
if (status && buttons == BLUE_BUTTON_N && !locked) {
|
||||
xTaskNotify(t_click, 0, eSetValueWithOverwrite);
|
||||
status = 0;
|
||||
ESP_LOGD(TAG, "button released");
|
||||
return ;
|
||||
}
|
||||
|
||||
/* stick button on right click */
|
||||
if (status == BLUE_BUTTON_N && buttons == BLUE_BUTTON_2 && !locked) {
|
||||
xTaskNotify(t_click, 1, eSetValueWithOverwrite);
|
||||
locked = true;
|
||||
releasable = false;
|
||||
ESP_LOGD(TAG, "button locked");
|
||||
return ;
|
||||
}
|
||||
|
||||
/* press button (simple click) */
|
||||
if (status == BLUE_BUTTON_N && buttons) {
|
||||
xTaskNotify(t_click, 1, eSetValueWithOverwrite);
|
||||
locked = false;
|
||||
status = 1;
|
||||
ESP_LOGD(TAG, "button pressed");
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Bluetooth common init: init module and various stuff
|
||||
* Host or Device specific inits go in blue_d_init() or blue_h_init()
|
||||
*/
|
||||
void blue_init(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Starting Bluetooth init on core %d", xPortGetCoreID());
|
||||
ESP_ERROR_CHECK(ret);
|
||||
ESP_ERROR_CHECK(esp_hid_gap_init(ESP_BT_MODE_CLASSIC_BT));
|
||||
|
||||
esp_log_level_set("event", ESP_LOG_INFO);
|
||||
/* complains about wrong data len on BOOT mode and CCONTROL */
|
||||
esp_log_level_set("BT_HIDH", ESP_LOG_VERBOSE);
|
||||
|
||||
/*
|
||||
* at this point, everything but bluetooth is started.
|
||||
* put green steady, start blinking blue and keep scanning until a device is found
|
||||
*
|
||||
* starting IDF 5.2.2, sometimes the first xTaskNotify() call may be lost. why ?
|
||||
*/
|
||||
|
||||
xTaskNotify(t_green, LED_ON, eSetValueWithOverwrite);
|
||||
xTaskNotify(t_green, LED_ON, eSetValueWithOverwrite);
|
||||
xTaskNotify(t_blue, LED_FAST, eSetValueWithOverwrite);
|
||||
|
||||
if (adb_is_host())
|
||||
blue_d_init();
|
||||
else
|
||||
blue_h_init();
|
||||
}
|
||||
|
||||
void blue_h_open(esp_hidh_event_data_t *p) {
|
||||
const uint8_t *bda = NULL;
|
||||
bool generic = true;
|
||||
|
||||
configASSERT(p != NULL);
|
||||
uint16_t vid = esp_hidh_dev_vendor_id_get(p->open.dev);
|
||||
uint16_t pid = esp_hidh_dev_product_id_get(p->open.dev);
|
||||
bda = esp_hidh_dev_bda_get(p->open.dev);
|
||||
ESP_LOGI(TAG, "opened connection with " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(bda));
|
||||
|
||||
/* Dump various info on console */
|
||||
blue_hid_rm_get(p->open.dev);
|
||||
blue_set_boot_protocol(p->open.dev);
|
||||
if (blue_support_boot(p->open.dev) == false) {
|
||||
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
|
||||
ESP_LOGE(TAG, "Mouse does not support boot protocol !");
|
||||
if (wii_is_wiimote(vid, pid)) {
|
||||
wii_open(bda);
|
||||
generic = false;
|
||||
}
|
||||
|
||||
/* Dump various info on console for generic devices */
|
||||
if (generic) {
|
||||
blue_hid_rm_get(p->open.dev);
|
||||
blue_set_boot_protocol(p->open.dev);
|
||||
if (blue_support_boot(p->open.dev) == false) {
|
||||
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
|
||||
ESP_LOGE(TAG, "Mouse does not support boot protocol !");
|
||||
}
|
||||
}
|
||||
|
||||
blue_pointers++;
|
||||
@ -520,7 +578,7 @@ static esp_hid_report_item_t blue_h_ri_find(esp_hidh_dev_t *d, esp_hid_usage_t u
|
||||
memset(&search, 0, sizeof(esp_hid_report_item_t));
|
||||
|
||||
for (uint8_t i = 0; i < num_reports; i++) {
|
||||
if (reports[i].protocol_mode == p && reports[i].report_type == t && reports[i].usage == u) {
|
||||
if (reports[i].protocol_mode == p && reports[i].report_type == t && reports[i].usage == u) {
|
||||
memcpy(&search, &reports[i], sizeof(esp_hid_report_item_t));
|
||||
break;
|
||||
}
|
||||
@ -562,49 +620,61 @@ static esp_hid_raw_report_map_t *blue_hid_rm_get(esp_hidh_dev_t *dev) {
|
||||
}
|
||||
|
||||
void blue_scan(void *pvParameters) {
|
||||
size_t len = 0;
|
||||
esp_hid_scan_result_t *mouse = NULL;
|
||||
esp_hid_scan_result_t *results = NULL;
|
||||
size_t len = 0;
|
||||
esp_hid_scan_result_t *mouse = NULL;
|
||||
esp_hid_scan_result_t *results = NULL;
|
||||
esp_hid_scan_result_t *wii = NULL;
|
||||
|
||||
ESP_LOGI(TAG, "starting scan on core %d…", xPortGetCoreID());
|
||||
esp_hid_scan(BLUE_SCAN_DURATION, &len, &results);
|
||||
ESP_LOGI(TAG, "scan returned %u result(s)", len);
|
||||
ESP_LOGI(TAG, "starting scan on core %d…", xPortGetCoreID());
|
||||
esp_hid_scan(BLUE_SCAN_DURATION, &len, &results);
|
||||
ESP_LOGI(TAG, "scan returned %u result(s)", len);
|
||||
|
||||
/* don't put the slow blink is a device reconnected while scanning */
|
||||
if (blue_pointers == 0)
|
||||
xTaskNotify(t_blue, LED_SLOW, eSetValueWithOverwrite);
|
||||
|
||||
if (len) {
|
||||
esp_hid_scan_result_t *r = results;
|
||||
if (len) {
|
||||
esp_hid_scan_result_t *r = results;
|
||||
|
||||
while (r) {
|
||||
/*
|
||||
* as of v1.4.5, esp_hid_gap will print detected devices in console (handle_bt_device_result())
|
||||
* just look for something that looks like Bluetooth Classic mouse
|
||||
*/
|
||||
/*
|
||||
* as of v1.4.5, esp_hid_gap will print detected devices in console (handle_bt_device_result())
|
||||
* just look for bluetooth classic devices that may be supported
|
||||
*/
|
||||
while (r) {
|
||||
if (r->transport == ESP_HID_TRANSPORT_BT &&
|
||||
strcmp("PERIPHERAL", esp_hid_cod_major_str(r->bt.cod.major)) == 0) {
|
||||
|
||||
if (r->transport == ESP_HID_TRANSPORT_BT &&
|
||||
strcmp("PERIPHERAL", esp_hid_cod_major_str(r->bt.cod.major)) == 0
|
||||
&& (r->bt.cod.minor & ESP_HID_COD_MIN_MOUSE)) {
|
||||
mouse = r;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
/* look for something that look like a mouse… */
|
||||
if (r->bt.cod.minor & ESP_HID_COD_MIN_MOUSE)
|
||||
mouse = r;
|
||||
|
||||
/* try to connect to the last candidate found */
|
||||
if (mouse)
|
||||
/* … or a wiimote (COD minor 1 is JOYSTICK) */
|
||||
if (r->bt.cod.minor == 1 && wii_is_nintendo_bda(r->bda))
|
||||
wii = r;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* try to connect to the last candidate found */
|
||||
if (mouse)
|
||||
#if CONFIG_BT_BLE_ENABLED
|
||||
esp_hidh_dev_open(mouse->bda, mouse->transport, mouse->ble.addr_type);
|
||||
esp_hidh_dev_open(mouse->bda, mouse->transport, mouse->ble.addr_type);
|
||||
#else
|
||||
esp_hidh_dev_open(mouse->bda, mouse->transport, NULL);
|
||||
esp_hidh_dev_open(mouse->bda, mouse->transport, 0);
|
||||
#endif
|
||||
else
|
||||
ESP_LOGI(TAG, "devices found but no mouse detected");
|
||||
if (wii)
|
||||
esp_hidh_dev_open(wii->bda, wii->transport, 0);
|
||||
|
||||
esp_hid_scan_results_free(results);
|
||||
}
|
||||
if (len && (!mouse && !wii)) {
|
||||
ESP_LOGI(TAG, "%i devices found, but no mouse or wiimote detected", len);
|
||||
esp_hid_scan_results_free(results);
|
||||
}
|
||||
if (len == 0) {
|
||||
ESP_LOGI(TAG, "no new mouse or wiimote detected");
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void blue_set_boot_protocol(esp_hidh_dev_t *dev) {
|
||||
|
12
main/blue.h
12
main/blue.h
@ -31,15 +31,17 @@
|
||||
#define BLUE_BUTTON_1 (1 << 0)
|
||||
#define BLUE_BUTTON_2 (1 << 1)
|
||||
#define BLUE_BUTTON_3 (1 << 2)
|
||||
#define BLUE_BUTTON_E 0xF8 /* shouldn't happen */
|
||||
#define BLUE_BUTTON_E 0xF8 /* shouldn't happen */
|
||||
|
||||
/* prototypes */
|
||||
void blue_adb2hid(void *pvParameters);
|
||||
void blue_init(void);
|
||||
void blue_h_input(esp_hidh_dev_t *dev, uint8_t *data, uint16_t length);
|
||||
void blue_h_close(esp_hidh_event_data_t *p);
|
||||
void blue_h_open(esp_hidh_event_data_t *p);
|
||||
void blue_scan(void *pvParameters);
|
||||
uint8_t blue_get_pin_code(esp_bd_addr_t bda, esp_bt_pin_code_t pin);
|
||||
void blue_h_boot(uint8_t *data, uint16_t length);
|
||||
void blue_h_input(esp_hidh_dev_t *dev, uint16_t id, uint8_t *data, uint16_t length);
|
||||
void blue_h_close(esp_hidh_event_data_t *p);
|
||||
void blue_h_open(esp_hidh_event_data_t *p);
|
||||
void blue_scan(void *pvParameters);
|
||||
void blue_set_boot_protocol(esp_hidh_dev_t *dev);
|
||||
|
||||
/* global variables for tasks handles */
|
||||
|
@ -243,7 +243,7 @@ void print_uuid(esp_bt_uuid_t *uuid)
|
||||
}
|
||||
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
static void handle_bt_device_result(struct disc_res_param *disc_res)
|
||||
void handle_bt_device_result(struct disc_res_param *disc_res)
|
||||
{
|
||||
GAP_DBG_PRINTF("BT : " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(disc_res->bda));
|
||||
uint32_t codv = 0;
|
||||
@ -381,16 +381,17 @@ static void handle_ble_device_result(struct ble_scan_result_evt_param *scan_rst)
|
||||
}
|
||||
#endif /* CONFIG_BT_BLE_ENABLED */
|
||||
|
||||
#warning here1
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
/*
|
||||
* BT GAP
|
||||
* */
|
||||
|
||||
#warning here2
|
||||
/* quack patch */
|
||||
uint8_t blue_get_pin_code(esp_bd_addr_t bda, esp_bt_pin_code_t pin);
|
||||
|
||||
void bt_gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
|
||||
{
|
||||
switch (event) {
|
||||
switch (event) {
|
||||
case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: {
|
||||
ESP_LOGV(TAG, "BT GAP DISC_STATE %s", (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) ? "START" : "STOP");
|
||||
if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) {
|
||||
@ -402,9 +403,36 @@ void bt_gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa
|
||||
handle_bt_device_result(¶m->disc_res);
|
||||
break;
|
||||
}
|
||||
#if (CONFIG_EXAMPLE_SSP_ENABLED)
|
||||
case ESP_BT_GAP_KEY_NOTIF_EVT:
|
||||
ESP_LOGI(TAG, "BT GAP KEY_NOTIF passkey:%"PRIu32, param->key_notif.passkey);
|
||||
break;
|
||||
case ESP_BT_GAP_CFM_REQ_EVT: {
|
||||
ESP_LOGI(TAG, "BT GAP CFM_REQ_EVT Please compare the numeric value: %"PRIu32, param->cfm_req.num_val);
|
||||
esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
|
||||
break;
|
||||
}
|
||||
case ESP_BT_GAP_KEY_REQ_EVT:
|
||||
ESP_LOGI(TAG, "BT GAP KEY_REQ_EVT Please enter passkey!");
|
||||
break;
|
||||
#endif
|
||||
case ESP_BT_GAP_MODE_CHG_EVT:
|
||||
ESP_LOGI(TAG, "BT GAP MODE_CHG_EVT mode:%d", param->mode_chg.mode);
|
||||
break;
|
||||
case ESP_BT_GAP_PIN_REQ_EVT: {
|
||||
ESP_LOGI(TAG, "BT GAP PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit);
|
||||
if (param->pin_req.min_16_digit) {
|
||||
ESP_LOGI(TAG, "Input pin code: 0000 0000 0000 0000");
|
||||
esp_bt_pin_code_t pin_code = {0};
|
||||
esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code);
|
||||
} else {
|
||||
esp_bt_pin_code_t pin_code;
|
||||
uint8_t pin_len;
|
||||
pin_len = blue_get_pin_code(param->pin_req.bda, pin_code);
|
||||
esp_bt_gap_pin_reply(param->pin_req.bda, true, pin_len, pin_code);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGW(TAG, "BT GAP EVENT %s", bt_gap_evt_str(event));
|
||||
break;
|
||||
@ -422,15 +450,11 @@ static esp_err_t init_bt_gap(void)
|
||||
#endif
|
||||
/*
|
||||
* Set default parameters for Legacy Pairing
|
||||
* Use fixed pin code
|
||||
* Use variable pin, input pin code when pairing
|
||||
*/
|
||||
esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED;
|
||||
esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE;
|
||||
esp_bt_pin_code_t pin_code;
|
||||
pin_code[0] = '0';
|
||||
pin_code[1] = '0';
|
||||
pin_code[2] = '0';
|
||||
pin_code[3] = '0';
|
||||
esp_bt_gap_set_pin(pin_type, 4, pin_code);
|
||||
esp_bt_gap_set_pin(pin_type, 0, pin_code);
|
||||
|
||||
if ((ret = esp_bt_gap_register_callback(bt_gap_event_handler)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_bt_gap_register_callback failed: %d", ret);
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_chip_info.h"
|
||||
#include "esp_gap_bt_api.h"
|
||||
|
||||
#include "adb.h"
|
||||
#include "blue.h"
|
||||
|
BIN
main/wii.aiff
Normal file
BIN
main/wii.aiff
Normal file
Binary file not shown.
868
main/wii.c
Normal file
868
main/wii.c
Normal file
@ -0,0 +1,868 @@
|
||||
/*
|
||||
* wii.c
|
||||
* quack
|
||||
*
|
||||
* Created by Michel DEPEIGE on 11/08/2024.
|
||||
* Copyright (c) 2020-2024 Michel DEPEIGE.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the Apache License, Version 2.0 (the "License");
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "driver/gpio.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_chip_info.h"
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "esp_bt_defs.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt_device.h"
|
||||
#include "esp_gap_bt_api.h"
|
||||
#endif
|
||||
#include "esp_hid_common.h"
|
||||
#include "esp_hidh.h"
|
||||
#include "esp_hidh_api.h"
|
||||
|
||||
#include "wii.h"
|
||||
#include "led.h"
|
||||
#include "gpio.h"
|
||||
|
||||
/* defines */
|
||||
#define TAG "WII"
|
||||
|
||||
/* globals */
|
||||
TaskHandle_t t_wii_dpad;
|
||||
extern TaskHandle_t t_click;
|
||||
extern TaskHandle_t t_yellow, t_red;
|
||||
extern QueueHandle_t q_qx, q_qy;
|
||||
|
||||
QueueHandle_t q_wii;
|
||||
|
||||
uint8_t wii_speed;
|
||||
wii_accel_t wii_calibration_0g;
|
||||
wii_accel_t wii_calibration_1g;
|
||||
|
||||
/* static defines */
|
||||
static void wii_accelerometer(esp_hidh_dev_t *dev, uint8_t *data);
|
||||
static void wii_change_mode(esp_hidh_dev_t *dev);
|
||||
static void wii_change_speed(esp_hidh_dev_t *dev, uint16_t buttons);
|
||||
static void wii_handle_button(uint16_t buttons);
|
||||
static void wii_mute_state(const esp_bd_addr_t bda, bool state);
|
||||
static void wii_rumble_state(const esp_bd_addr_t bda, bool state);
|
||||
static void wii_speaker_state(const esp_bd_addr_t bda, bool state);
|
||||
|
||||
/* functions */
|
||||
static void wii_accelerometer(esp_hidh_dev_t *dev, uint8_t *data) {
|
||||
const uint8_t *bda = NULL;
|
||||
static uint8_t p = 0; /* used for calibration pass */
|
||||
uint8_t report[3] = {0};
|
||||
esp_err_t ret;
|
||||
|
||||
float x, y, z;
|
||||
static float average_x, average_y, average_z;
|
||||
static uint16_t sum_x, sum_y, sum_z;
|
||||
static int8_t out; /* used for quadrature messages */
|
||||
wii_accel_t a = {0, 0, 0};
|
||||
|
||||
configASSERT(data != NULL);
|
||||
configASSERT(dev != NULL);
|
||||
bda = esp_hidh_dev_bda_get(dev);
|
||||
if (bda == NULL)
|
||||
return;
|
||||
/* extract accelerator data. see https://wiibrew.org/wiki/Wiimote#Accelerometer */
|
||||
a.x = (data[2] << 2) | ((data[0] >> 5) & 0x3);
|
||||
a.y = (data[3] << 2) | ((data[1] >> 4) & 0x2);
|
||||
a.z = (data[4] << 2) | ((data[1] >> 5) & 0x2);
|
||||
|
||||
/* generate acceleration values */
|
||||
x = ((float)a.x - (float)wii_calibration_0g.x) / (float)wii_calibration_1g.x;
|
||||
y = ((float)a.y - (float)wii_calibration_0g.y) / (float)wii_calibration_1g.y;
|
||||
z = ((float)a.z - (float)wii_calibration_0g.z) / (float)wii_calibration_1g.z;
|
||||
|
||||
/*
|
||||
* at this point it's 2AM and the values read from the test Wiimote don't match
|
||||
* the calibration values. we are limited since no IR / lightbar anyway so can't
|
||||
* get yaw
|
||||
*
|
||||
* dunno if there is something wrong in the code or the Wiimote is decalibrated
|
||||
*
|
||||
* so from no on, we will mangle the values to do a Arkanoid or Space Invaders
|
||||
* mode and call it a day (or a night)
|
||||
*
|
||||
* also ignore Y and Z from now on. Sorry Y and Z
|
||||
* inspiration from https://www.nxp.com/docs/en/application-note/AN3397.pdf
|
||||
*/
|
||||
|
||||
if (p == 0) {
|
||||
ESP_LOGD(TAG, "starting calibration…");
|
||||
|
||||
sum_x = 0;
|
||||
sum_y = 0;
|
||||
sum_z = 0;
|
||||
}
|
||||
if (p < 255) {
|
||||
/*
|
||||
* store MSB X, Y and Z values so the averages gets updated
|
||||
* this will override values read from EEPROM but they don't seem useable
|
||||
* for now (see above)
|
||||
*/
|
||||
|
||||
/* store MSB X and Y values so the average can get calculated later */
|
||||
sum_x += a.x >> 2;
|
||||
sum_y += a.y >> 2;
|
||||
sum_z += a.z >> 2;
|
||||
p++;
|
||||
|
||||
/* got enough samples, let's find averages */
|
||||
if (p == 255) {
|
||||
average_x = ((float)((sum_x << 2) / 255) - (float)wii_calibration_0g.x);
|
||||
average_x /= (float)wii_calibration_1g.x;
|
||||
average_y = ((float)((sum_y << 2) / 255) - (float)wii_calibration_0g.y);
|
||||
average_y /= (float)wii_calibration_1g.y;
|
||||
average_z = ((float)((sum_z << 2) / 255) - (float)wii_calibration_0g.z);
|
||||
average_z /= (float)wii_calibration_1g.z;
|
||||
|
||||
/* do a quick rumble as user notification */
|
||||
wii_rumble_state(bda, true);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
wii_rumble_state(bda, false);
|
||||
report[0] = WII_REPORT_LEDS;
|
||||
report[1] = WII_LED4;
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot set LEDs state, send error: %x", ret);
|
||||
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
|
||||
ESP_LOGD(TAG, "\t … X: %f, Y: %f, Z: %f", average_x, average_y, average_z);
|
||||
|
||||
/* set to events only after calibration */
|
||||
report[0] = WII_REPORT_MODE;
|
||||
report[1] = 0x00; /* events only report mode */
|
||||
report[2] = 0x31; /* core buttons + accelerometer */
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 3);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot set mode, send error: %x", ret);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* just avoid may be used uninitialized messages */
|
||||
(void)y;
|
||||
(void)z;
|
||||
|
||||
/*
|
||||
* do a mechanical filtering window, assuming the Wiimote is doing the low pass
|
||||
* filtering itself since main report rate is about 30 Hz
|
||||
* count idle passes to avoid drift, disregard half of what we did
|
||||
* previously. this is a crap. Now I understand why Motion Plus exists
|
||||
* this will move the mouse on the x axis, speed proportional to the x axis tilt
|
||||
*/
|
||||
if ((average_x - x) < -WII_THRESHOLD || (average_x - x) > WII_THRESHOLD) {
|
||||
/* cap value to not overflow 8 bit */
|
||||
out = (average_x - x) * WII_ACCELERATION * wii_speed;
|
||||
if (((average_x - x) * WII_ACCELERATION * wii_speed) < -120)
|
||||
out = -120;
|
||||
if (((average_x - x) * WII_ACCELERATION * wii_speed) > 120)
|
||||
out = 120;
|
||||
}
|
||||
else {
|
||||
out = out >> 1;
|
||||
}
|
||||
//ESP_LOGD(TAG, "X %f %f :: %i", (average_x - x), (average_x - x) * WII_ACCELERATION, out);
|
||||
|
||||
if (out)
|
||||
xQueueSendToBack(q_qx, &out, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* this will switch between the default report (buttons only) and the more advanced
|
||||
* mode with accelerometer data. We don't handle others advanced modes nor extensions
|
||||
*/
|
||||
static void wii_change_mode(esp_hidh_dev_t *dev) {
|
||||
static bool accel = 0;
|
||||
const uint8_t *bda = NULL;
|
||||
uint8_t report[3] = {0};
|
||||
esp_err_t ret;
|
||||
|
||||
configASSERT(dev != NULL);
|
||||
bda = esp_hidh_dev_bda_get(dev);
|
||||
if (bda == NULL)
|
||||
return;
|
||||
|
||||
accel = !accel;
|
||||
ESP_LOGD(TAG, "changing mode, setting accelerometer status to %x", accel);
|
||||
report[0] = WII_REPORT_MODE;
|
||||
if (accel) {
|
||||
report[1] = 0x04; /* continuous report mode */
|
||||
report[2] = 0x31; /* core buttons + accelerometer */
|
||||
}
|
||||
else {
|
||||
report[1] = 0x00; /* events only report mode */
|
||||
report[2] = 0x30; /* core buttons only */
|
||||
}
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 3);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot set mode, send error: %x", ret);
|
||||
|
||||
wii_rumble_state(bda, true);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
wii_rumble_state(bda, false);
|
||||
|
||||
/* disable speed LEDs on accelerator mode (or restore in default mode) */
|
||||
report[0] = WII_REPORT_LEDS;
|
||||
if (accel)
|
||||
report[1] = WII_LED4;
|
||||
else
|
||||
report[1] = wii_speed | WII_LED4;
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot set LEDs state, send error: %x", ret);
|
||||
}
|
||||
|
||||
static void wii_change_speed(esp_hidh_dev_t *dev, uint16_t buttons) {
|
||||
const uint8_t *bda = NULL;
|
||||
uint8_t report[2] = {0};
|
||||
esp_err_t ret;
|
||||
|
||||
configASSERT(dev != NULL);
|
||||
bda = esp_hidh_dev_bda_get(dev);
|
||||
if (bda == NULL)
|
||||
return;
|
||||
|
||||
/* user facerolling the wiimote, ignore */
|
||||
if ((buttons & WII_BUTTON_PLUS) && (buttons & WII_BUTTON_MINUS))
|
||||
return;
|
||||
|
||||
if (buttons & WII_BUTTON_MINUS) {
|
||||
switch (wii_speed) {
|
||||
case WII_SPEED_NONE:
|
||||
case WII_SPEED_LOW:
|
||||
return;
|
||||
case WII_SPEED_MEDIUM:
|
||||
wii_speed = WII_SPEED_LOW;
|
||||
break;
|
||||
case WII_SPEED_HIGH:
|
||||
wii_speed = WII_SPEED_MEDIUM;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (buttons & WII_BUTTON_PLUS) {
|
||||
switch (wii_speed) {
|
||||
case WII_SPEED_NONE:
|
||||
return;
|
||||
case WII_SPEED_LOW:
|
||||
wii_speed = WII_SPEED_MEDIUM;
|
||||
break;
|
||||
case WII_SPEED_MEDIUM:
|
||||
wii_speed = WII_SPEED_HIGH;
|
||||
break;
|
||||
case WII_SPEED_HIGH:
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* set LEDs for new speed */
|
||||
report[0] = WII_REPORT_LEDS;
|
||||
report[1] = wii_speed | WII_LED4;
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot set LEDs state, send error: %x", ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* this will enable the speaker, play the boot chime and then disable the speaker
|
||||
* this is blocking for about 400-600ms
|
||||
*/
|
||||
void wii_chime(const esp_bd_addr_t bda) {
|
||||
uint8_t report[22];
|
||||
esp_err_t ret;
|
||||
uint8_t *pos;
|
||||
|
||||
/* dual note bleepbleepX2 extracted from wii.aiff */
|
||||
const unsigned char chime[] = {
|
||||
0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff,
|
||||
0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x01, 0xfc,
|
||||
0xfd, 0xff, 0xfd, 0xf6, 0x09, 0x18, 0x02, 0x05, 0x17, 0x02,
|
||||
0xcb, 0xd8, 0x18, 0x3c, 0x13, 0xcf, 0xc8, 0x05, 0x40, 0x29,
|
||||
0xe0, 0xbe, 0xf0, 0x32, 0x34, 0xf0, 0xc1, 0xe1, 0x2a, 0x3c,
|
||||
0x02, 0xc3, 0xd0, 0x19, 0x42, 0x18, 0xcf, 0xc3, 0x04, 0x40,
|
||||
0x29, 0xde, 0xbb, 0xef, 0x3a, 0x3c, 0xf0, 0xb8, 0xda, 0x2b,
|
||||
0x45, 0x09, 0xc1, 0xc8, 0x15, 0x40, 0x1c, 0xd7, 0xc2, 0xff,
|
||||
0x43, 0x1d, 0xdc, 0xdd, 0xe2, 0x09, 0x49, 0x16, 0xb6, 0xdf,
|
||||
0x39, 0x1c, 0xcb, 0xeb, 0x35, 0x0f, 0xc9, 0xf1, 0x3b, 0x0c,
|
||||
0xc3, 0xf6, 0x3c, 0x05, 0xc3, 0xfd, 0x3b, 0x00, 0xc5, 0x03,
|
||||
0x3b, 0xf8, 0xc2, 0x0a, 0x3e, 0xf2, 0xc0, 0x10, 0x3f, 0xed,
|
||||
0xc1, 0x15, 0x3d, 0xe8, 0xc3, 0x1a, 0x3a, 0xe2, 0xc6, 0x20,
|
||||
0x38, 0xdc, 0xc9, 0x25, 0x34, 0xd8, 0xcd, 0x2a, 0x30, 0xd3,
|
||||
0xd1, 0x2e, 0x2c, 0xcf, 0xd5, 0x32, 0x27, 0xcb, 0xda, 0x36,
|
||||
0x23, 0xc8, 0xdf, 0x39, 0x1d, 0xc5, 0xe5, 0x3c, 0x18, 0xc2,
|
||||
0xea, 0x3e, 0x12, 0xc1, 0xf0, 0x3f, 0x0c, 0xbf, 0xf6, 0x40,
|
||||
0x06, 0xbe, 0xfc, 0x41, 0x00, 0xbe, 0x02, 0x41, 0xfa, 0xbf,
|
||||
0x08, 0x40, 0xf4, 0xbf, 0x0e, 0x3f, 0xee, 0xc1, 0x14, 0x3d,
|
||||
0xe8, 0xc3, 0x1a, 0x3b, 0xe3, 0xc5, 0x1f, 0x38, 0xde, 0xc9,
|
||||
0x24, 0x35, 0xd8, 0xcc, 0x29, 0x31, 0xd4, 0xd0, 0x2d, 0x2d,
|
||||
0xcf, 0xd4, 0x32, 0x28, 0xcc, 0xd9, 0x35, 0x23, 0xc8, 0xde,
|
||||
0x39, 0x1e, 0xc5, 0xe4, 0x3b, 0x19, 0xc3, 0xe9, 0x3d, 0x12,
|
||||
0xc1, 0xef, 0x3e, 0x0d, 0xc0, 0xf5, 0x3f, 0x07, 0xbf, 0xfb,
|
||||
0x40, 0x01, 0xc0, 0x01, 0x3e, 0xfb, 0xc1, 0x06, 0x3b, 0xf6,
|
||||
0xc5, 0x0b, 0x37, 0xf2, 0xcc, 0x12, 0x2e, 0xf1, 0xda, 0x0b,
|
||||
0x0c, 0xf3, 0x12, 0x13, 0x00, 0x09, 0x18, 0xf5, 0xc6, 0xe5,
|
||||
0x25, 0x3a, 0x02, 0xc7, 0xd1, 0x17, 0x43, 0x1a, 0xd1, 0xc3,
|
||||
0x01, 0x3b, 0x28, 0xe0, 0xc1, 0xf2, 0x35, 0x34, 0xf0, 0xbe,
|
||||
0xde, 0x28, 0x3f, 0x06, 0xc4, 0xcd, 0x16, 0x42, 0x1a, 0xcf,
|
||||
0xc0, 0x02, 0x43, 0x2f, 0xdd, 0xb8, 0xec, 0x39, 0x3e, 0xf6,
|
||||
0xba, 0xd7, 0x26, 0x3f, 0x0c, 0xcc, 0xca, 0x13, 0x45, 0x0a,
|
||||
0xd8, 0xdf, 0xe5, 0x1b, 0x4a, 0xfc, 0xb2, 0xf7, 0x3f, 0x07,
|
||||
0xc6, 0x00, 0x37, 0xfb, 0xc6, 0x06, 0x3c, 0xf5, 0xc3, 0x0c,
|
||||
0x3b, 0xef, 0xc6, 0x12, 0x38, 0xeb, 0xc9, 0x18, 0x35, 0xe3,
|
||||
0xc9, 0x1f, 0x36, 0xdd, 0xc9, 0x25, 0x34, 0xd8, 0xcd, 0x29,
|
||||
0x31, 0xd3, 0xd0, 0x2e, 0x2c, 0xcf, 0xd5, 0x32, 0x28, 0xcb,
|
||||
0xda, 0x36, 0x23, 0xc8, 0xdf, 0x39, 0x1e, 0xc5, 0xe4, 0x3b,
|
||||
0x18, 0xc2, 0xea, 0x3d, 0x12, 0xc1, 0xf0, 0x3f, 0x0d, 0xbf,
|
||||
0xf5, 0x40, 0x07, 0xbe, 0xfc, 0x41, 0x00, 0xbe, 0x01, 0x41,
|
||||
0xfa, 0xbf, 0x08, 0x40, 0xf4, 0xbf, 0x0d, 0x3f, 0xef, 0xc1,
|
||||
0x13, 0x3d, 0xe9, 0xc3, 0x19, 0x3b, 0xe3, 0xc5, 0x1f, 0x38,
|
||||
0xde, 0xc8, 0x24, 0x35, 0xd9, 0xcc, 0x29, 0x31, 0xd4, 0xd0,
|
||||
0x2d, 0x2d, 0xd0, 0xd4, 0x31, 0x29, 0xcc, 0xd9, 0x35, 0x24,
|
||||
0xc8, 0xde, 0x38, 0x1f, 0xc5, 0xe3, 0x3b, 0x19, 0xc3, 0xe9,
|
||||
0x3d, 0x13, 0xc1, 0xef, 0x3f, 0x0d, 0xbf, 0xf4, 0x40, 0x08,
|
||||
0xbe, 0xfb, 0x41, 0x01, 0xbe, 0x01, 0x40, 0xfb, 0xbf, 0x07,
|
||||
0x3f, 0xf5, 0xc0, 0x0d, 0x3e, 0xef, 0xc1, 0x13, 0x3c, 0xea,
|
||||
0xc5, 0x18, 0x39, 0xe5, 0xc8, 0x1b, 0x35, 0xe2, 0xcd, 0x1f,
|
||||
0x2f, 0xdf, 0xd7, 0x22, 0x25, 0xe4, 0xe3, 0x11, 0x18, 0xfa,
|
||||
0xef, 0x04, 0x07, 0xfe, 0xfd, 0x01, 0x00, 0xff, 0xff, 0xff,
|
||||
0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff
|
||||
};
|
||||
|
||||
/*
|
||||
* do the initialisation sequence for the speaker:
|
||||
* - enable speaker
|
||||
* - mute speaker
|
||||
* - setup registers & voodoo
|
||||
* - unmute speaker
|
||||
*/
|
||||
configASSERT(bda != NULL);
|
||||
wii_speaker_state(bda, true);
|
||||
wii_mute_state(bda, true);
|
||||
|
||||
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
|
||||
memset(report, 0, 22);
|
||||
report[0] = WII_REPORT_WRITE_REG;
|
||||
report[1] = 0x04;
|
||||
report[2] = 0xa2;
|
||||
report[3] = 0x00;
|
||||
report[4] = 0x09;
|
||||
report[5] = 1;
|
||||
report[6] = 0x01;
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 22);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot set register, send error: %x", ret);
|
||||
|
||||
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
|
||||
memset(report, 0, 22);
|
||||
report[0] = WII_REPORT_WRITE_REG;
|
||||
report[1] = 0x04;
|
||||
report[2] = 0xa2;
|
||||
report[3] = 0x00;
|
||||
report[4] = 0x01;
|
||||
report[5] = 1;
|
||||
report[6] = 0x08;
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 22);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot set register, send error: %x", ret);
|
||||
|
||||
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
|
||||
memset(report, 0, 22);
|
||||
report[0] = WII_REPORT_WRITE_REG;
|
||||
report[1] = 0x04;
|
||||
report[2] = 0xa2;
|
||||
report[3] = 0x00;
|
||||
report[4] = 0x01;
|
||||
report[5] = 7;
|
||||
report[6] = 0x00;
|
||||
report[7] = 0x40; /* set PCM mode */
|
||||
report[8] = 0xe0; /* set 1 kHz sample rate */
|
||||
report[9] = 0x2e; /* set 1 kHz sample rate */
|
||||
report[10] = 0xe0; /* increase default volume */
|
||||
report[11] = 0x00;
|
||||
report[12] = 0x00;
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 22);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot set register, send error: %x", ret);
|
||||
|
||||
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
|
||||
memset(report, 0, 22);
|
||||
report[0] = WII_REPORT_WRITE_REG;
|
||||
report[1] = 0x04;
|
||||
report[2] = 0xa2;
|
||||
report[3] = 0x00;
|
||||
report[4] = 0x08;
|
||||
report[5] = 1;
|
||||
report[6] = 0x01;
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 22);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot set register, send error: %x", ret);
|
||||
|
||||
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
|
||||
wii_mute_state(bda, false);
|
||||
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
|
||||
memset(report, 0, 22);
|
||||
report[0] = WII_REPORT_SPEAKER_D;
|
||||
report[1] = 20 << 3; /* 20 bytes each round (20 8bits samples) */
|
||||
for (pos = (uint8_t *)chime ; pos < (chime + sizeof(chime)) ; pos += 20) {
|
||||
memcpy(report + 2 * sizeof(uint8_t), pos, 20);
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 22);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot stream PCM data, send error: %x", ret);
|
||||
/* max 1kHz 8bit PCM is at best what we can stream */
|
||||
vTaskDelay(WII_THROTTLE / portTICK_PERIOD_MS);
|
||||
}
|
||||
wii_speaker_state(bda, false);
|
||||
}
|
||||
|
||||
/* this function will generate movement notifications while a button is keep pressed */
|
||||
void wii_dpad(void *pvParameters) {
|
||||
uint16_t buttons = 0;
|
||||
uint32_t notify = 0;
|
||||
int8_t x, y;
|
||||
TickType_t delay = portMAX_DELAY;
|
||||
|
||||
(void)pvParameters;
|
||||
|
||||
x = 0;
|
||||
y = 0;
|
||||
|
||||
while (true) {
|
||||
if (xTaskNotifyWait(0, 0, ¬ify, delay) == pdTRUE) {
|
||||
/* we received a status change */
|
||||
buttons = (uint16_t)notify;
|
||||
|
||||
if (! buttons) {
|
||||
delay = portMAX_DELAY;
|
||||
continue;
|
||||
}
|
||||
|
||||
delay = WII_INTERVAL / portTICK_PERIOD_MS;
|
||||
}
|
||||
|
||||
/* ignore facerolling */
|
||||
if ((buttons & WII_BUTTON_UP) && (buttons & WII_BUTTON_DOWN))
|
||||
continue;
|
||||
if ((buttons & WII_BUTTON_LEFT) && (buttons & WII_BUTTON_RIGHT))
|
||||
continue;
|
||||
|
||||
if (buttons & WII_BUTTON_LEFT) {
|
||||
x = -((wii_speed >> 4) * 4);
|
||||
xQueueSendToBack(q_qx, &x, 0);
|
||||
}
|
||||
if (buttons & WII_BUTTON_RIGHT) {
|
||||
x = ((wii_speed >> 4) * 4);
|
||||
xQueueSendToBack(q_qx, &x, 0);
|
||||
}
|
||||
if (buttons & WII_BUTTON_UP) {
|
||||
y = -((wii_speed >> 4) * 4);
|
||||
xQueueSendToBack(q_qy, &y, 0);
|
||||
}
|
||||
if (buttons & WII_BUTTON_DOWN) {
|
||||
y = ((wii_speed >> 4) * 4);
|
||||
xQueueSendToBack(q_qy, &y, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* generate the PIN code needed for pairing. Info from https://wiibrew.org/wiki/Wiimote
|
||||
* If connecting by holding down the 1+2 buttons, the PIN is the bluetooth address of
|
||||
* the wiimote backwards, if connecting by pressing the "sync" button on the back of
|
||||
* the wiimote, then the PIN is the bluetooth address of the host backwards.
|
||||
*
|
||||
* we will implement the last option
|
||||
*/
|
||||
uint8_t *wii_generate_pin(esp_bt_pin_code_t pin) {
|
||||
uint8_t *mac;
|
||||
|
||||
configASSERT(pin != NULL);
|
||||
mac = (uint8_t *)esp_bt_dev_get_address();
|
||||
if (mac == NULL)
|
||||
return NULL;
|
||||
|
||||
pin[0] = mac[5];
|
||||
pin[1] = mac[4];
|
||||
pin[2] = mac[3];
|
||||
pin[3] = mac[2];
|
||||
pin[4] = mac[1];
|
||||
pin[5] = mac[0];
|
||||
ESP_LOGV(TAG, "Bluetooth MAC: " ESP_BD_ADDR_STR ", PIN code: " WII_PIN_STR,
|
||||
ESP_BD_ADDR_HEX(mac), WII_PIN_HEX(pin));
|
||||
|
||||
return mac;
|
||||
}
|
||||
|
||||
/*
|
||||
* similar to blue_handle_button
|
||||
* this will only handle buttons that are used for clicks
|
||||
* buttons that are used for movement are handled by the wii_dpad task
|
||||
*/
|
||||
|
||||
static void wii_handle_button(uint16_t buttons) {
|
||||
static bool locked = false;
|
||||
static bool releasable = true;
|
||||
static bool status = WII_BUTTON_NONE; // Keep state
|
||||
|
||||
buttons = buttons & (WII_BUTTON_A | WII_BUTTON_B);
|
||||
|
||||
if (status && buttons)
|
||||
return ;
|
||||
if (status == WII_BUTTON_NONE && buttons == WII_BUTTON_NONE && !locked)
|
||||
return ;
|
||||
if (buttons == WII_BUTTON_A && locked && !releasable)
|
||||
return ;
|
||||
if (status == WII_BUTTON_NONE && buttons == WII_BUTTON_NONE && locked) {
|
||||
releasable = true;
|
||||
return ;
|
||||
}
|
||||
|
||||
/* release button */
|
||||
if (status && buttons == WII_BUTTON_NONE && !locked) {
|
||||
xTaskNotify(t_click, 0, eSetValueWithOverwrite);
|
||||
status = 0;
|
||||
ESP_LOGD(TAG, "button released");
|
||||
return ;
|
||||
}
|
||||
|
||||
/* stick button on right click */
|
||||
if (status == WII_BUTTON_NONE && buttons == WII_BUTTON_A && !locked) {
|
||||
xTaskNotify(t_click, 1, eSetValueWithOverwrite);
|
||||
locked = true;
|
||||
releasable = false;
|
||||
ESP_LOGD(TAG, "button locked");
|
||||
return ;
|
||||
}
|
||||
|
||||
/* press button (simple click) */
|
||||
if (status == WII_BUTTON_NONE && buttons) {
|
||||
xTaskNotify(t_click, 1, eSetValueWithOverwrite);
|
||||
locked = false;
|
||||
status = 1;
|
||||
ESP_LOGD(TAG, "button pressed");
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
void wii_init(void) {
|
||||
/* put default values into calibration data */
|
||||
memset(&wii_calibration_0g, 0, sizeof(wii_accel_t));
|
||||
memset(&wii_calibration_1g, 0, sizeof(wii_accel_t));
|
||||
|
||||
xTaskCreate(wii_dpad, "WII_DPAD", 1024, NULL, tskIDLE_PRIORITY + 1, &t_wii_dpad);
|
||||
}
|
||||
|
||||
void wii_input(esp_hidh_dev_t *dev, uint16_t id, uint8_t *data, uint16_t length) {
|
||||
uint16_t buttons = 0;
|
||||
static bool changed = 0;
|
||||
|
||||
/* do some basic sanity checks then ignore every report we cannot handle */
|
||||
if (id == WII_REPORT_CORE_ONLY && length != 2) {
|
||||
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
|
||||
return ;
|
||||
}
|
||||
if (id == WII_REPORT_ACK && length != 4) {
|
||||
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
|
||||
return ;
|
||||
}
|
||||
if (id == WII_REPORT_CORE_ACCEL && length != 5) {
|
||||
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
|
||||
return ;
|
||||
}
|
||||
if (id == WII_REPORT_STATUS && length != 6) {
|
||||
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
|
||||
return ;
|
||||
}
|
||||
if (id == WII_REPORT_READ_INPUT && length != 21) {
|
||||
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
|
||||
return ;
|
||||
}
|
||||
|
||||
if (id != WII_REPORT_ACK && id != WII_REPORT_CORE_ONLY &&
|
||||
id != WII_REPORT_STATUS && id != WII_REPORT_CORE_ACCEL &&
|
||||
id != WII_REPORT_READ_INPUT) {
|
||||
ESP_LOGW(TAG, "Unknown report id 0x%x received (size: %i)", id, length);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* handle error codes or specific stuff from the wiimote
|
||||
* we can continue to handle core buttons as usual later
|
||||
*/
|
||||
switch (id) {
|
||||
case WII_REPORT_STATUS:
|
||||
/* format is BB BB LF 00 00 VV (buttons, flags, battery) */
|
||||
ESP_LOGD(TAG, "status: 0x%x%x 0x%x %i", data[0], data[1], data[2], data[5]);
|
||||
break;
|
||||
case WII_REPORT_READ_INPUT:
|
||||
/* assume a calibration read, since it's the only read request done */
|
||||
wii_show_calibration(data);
|
||||
break;
|
||||
case WII_REPORT_ACK:
|
||||
/* format is BB BB RR EE (buttons, report number, error code) */
|
||||
if (data[3]) {
|
||||
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
|
||||
ESP_LOGE(TAG, "error with report id 0x%x, code %x", data[2], data[3]);
|
||||
}
|
||||
break;
|
||||
case WII_REPORT_CORE_ACCEL:
|
||||
wii_accelerometer(dev, data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
buttons = (data[1] << 8) | data[0];
|
||||
/* changing speed is disabled in accelerometer mode */
|
||||
if ((buttons & WII_BUTTON_PLUS || buttons & WII_BUTTON_MINUS) && length == 2)
|
||||
wii_change_speed(dev, buttons);
|
||||
|
||||
/* swap modes (enable / disable accelerometer) */
|
||||
if (buttons & WII_BUTTON_HOME) {
|
||||
if (! changed) {
|
||||
wii_change_mode(dev);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed && !(buttons & WII_BUTTON_HOME))
|
||||
changed = false;
|
||||
|
||||
/* do the buttons things */
|
||||
wii_handle_button(buttons);
|
||||
xTaskNotify(t_wii_dpad, (uint32_t)buttons, eSetValueWithOverwrite);
|
||||
}
|
||||
|
||||
/*
|
||||
* this will compare the provided BDA to a list of known Nintendo list
|
||||
* return true if the provided BDA is a known prefix or fakse otherwise
|
||||
*/
|
||||
bool wii_is_nintendo_bda(esp_bd_addr_t bda) {
|
||||
uint8_t i = 0;
|
||||
|
||||
/* known "Nintendo Co., Ltd." BDA prefixes as of 08/2024 */
|
||||
const esp_bd_addr_t nintendo_bda_list[] = {
|
||||
{0x00, 0x09, 0xbf, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x16, 0x56, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x17, 0xab, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x19, 0x1d, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x19, 0xfd, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x1a, 0xe9, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x1b, 0x7a, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x1b, 0xea, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x1c, 0xbe, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x1d, 0xbc, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x1e, 0x35, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x1e, 0xa9, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x1f, 0x32, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x1f, 0xc5, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x21, 0x47, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x21, 0xbd, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x22, 0x4c, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x22, 0xaa, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x22, 0xd7, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x23, 0x31, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x23, 0xcc, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x24, 0x1e, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x24, 0x44, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x24, 0xf3, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x25, 0xa0, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x26, 0x59, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x27, 0x09, 0x00, 0x00, 0x00},
|
||||
{0x18, 0x2a, 0x7b, 0x00, 0x00, 0x00},
|
||||
{0x2c, 0x10, 0xc1, 0x00, 0x00, 0x00},
|
||||
{0x34, 0xaf, 0x2c, 0x00, 0x00, 0x00},
|
||||
{0x40, 0xd2, 0x8a, 0x00, 0x00, 0x00},
|
||||
{0x40, 0xf4, 0x07, 0x00, 0x00, 0x00},
|
||||
{0x58, 0xbd, 0xa3, 0x00, 0x00, 0x00},
|
||||
{0x78, 0xa2, 0xa0, 0x00, 0x00, 0x00},
|
||||
{0x7c, 0xbb, 0x8a, 0x00, 0x00, 0x00},
|
||||
{0x8c, 0x56, 0xc5, 0x00, 0x00, 0x00},
|
||||
{0x8c, 0xcd, 0xe8, 0x00, 0x00, 0x00},
|
||||
{0x9c, 0xe6, 0x35, 0x00, 0x00, 0x00},
|
||||
{0xa4, 0x5c, 0x27, 0x00, 0x00, 0x00},
|
||||
{0xa4, 0xc0, 0xe1, 0x00, 0x00, 0x00},
|
||||
{0xb8, 0xae, 0x6e, 0x00, 0x00, 0x00},
|
||||
{0xcc, 0x9e, 0x00, 0x00, 0x00, 0x00},
|
||||
{0xcc, 0xfb, 0x65, 0x00, 0x00, 0x00},
|
||||
{0xd8, 0x6b, 0xf7, 0x00, 0x00, 0x00},
|
||||
{0xe0, 0x0c, 0x7f, 0x00, 0x00, 0x00},
|
||||
{0xe0, 0xe7, 0x51, 0x00, 0x00, 0x00},
|
||||
{0xe8, 0x4e, 0xce, 0x00, 0x00, 0x00},
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||
};
|
||||
|
||||
while (nintendo_bda_list[i][0] || nintendo_bda_list[i][1] || nintendo_bda_list[i][2]) {
|
||||
if (nintendo_bda_list[i][0] == bda[0] &&
|
||||
nintendo_bda_list[i][1] == bda[1] &&
|
||||
nintendo_bda_list[i][2] == bda[2]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* this will tell us if the device is a Wiimote. PID + VID provided by the HID stack */
|
||||
bool wii_is_wiimote(uint16_t vid, uint16_t pid) {
|
||||
if (vid != WII_VID)
|
||||
return false;
|
||||
if (pid != WII_PID_OLD && pid != WII_PID_NEW)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* this is called every time a Wiimote is connected */
|
||||
void wii_open(const esp_bd_addr_t bda) {
|
||||
uint8_t report[7] = {0};
|
||||
esp_err_t ret;
|
||||
|
||||
configASSERT(bda != NULL);
|
||||
/* enable LED4 on connect */
|
||||
report[0] = WII_REPORT_LEDS;
|
||||
report[1] = WII_LED4;
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot set LED4 state, send error: %x", ret);
|
||||
|
||||
/* set initial LEDs state */
|
||||
wii_speed = 0;
|
||||
|
||||
/* stream the boot chime to the wii mote. this will also wake up the rumble */
|
||||
wii_chime(bda);
|
||||
wii_rumble_state(bda, true);
|
||||
vTaskDelay(400 / portTICK_PERIOD_MS);
|
||||
wii_speed = WII_SPEED_MEDIUM;
|
||||
wii_rumble_state(bda, false);
|
||||
|
||||
/* do a read to fetch accelerometer calibration data from both spaces */
|
||||
report[0] = WII_REPORT_READ_REG;
|
||||
report[1] = 0x00; /* EEPROM space */
|
||||
report[2] = 0x00; /* read from 0x000016 */
|
||||
report[3] = 0x00; /* read from 0x000016 */
|
||||
report[4] = 0x16; /* read from 0x000016 */
|
||||
report[5] = 0x00; /* read 10 bytes */
|
||||
report[6] = 0x0a; /* read 10 bytes */
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 7);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot read accelerometer calibration, send error: %x", ret);
|
||||
report[0] = WII_REPORT_READ_REG;
|
||||
report[1] = 0x00; /* EEPROM space */
|
||||
report[2] = 0x00; /* read from 0x000020 */
|
||||
report[3] = 0x00; /* read from 0x000020 */
|
||||
report[4] = 0x20; /* read from 0x000020 */
|
||||
report[5] = 0x00; /* read 10 bytes */
|
||||
report[6] = 0x0a; /* read 10 bytes */
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 7);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot read accelerometer calibration, send error: %x", ret);
|
||||
}
|
||||
|
||||
/* mute the speaker on the Wiimote. this will clear the rumble ! */
|
||||
static void wii_mute_state(const esp_bd_addr_t bda, bool state) {
|
||||
uint8_t report[2] = {0};
|
||||
esp_err_t ret;
|
||||
|
||||
configASSERT(bda != NULL);
|
||||
ESP_LOGV(TAG, "setting mute to %x", state);
|
||||
report[0] = WII_REPORT_SPEAKER_M;
|
||||
if (state)
|
||||
report[1] = 0x04;
|
||||
else
|
||||
report[1] = 0x0;
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot set mute state, send error: %x", ret);
|
||||
}
|
||||
|
||||
/* enable the rumble on the Wiimote. this will clear the LEDs so keep the LEDs state */
|
||||
static void wii_rumble_state(const esp_bd_addr_t bda, bool state) {
|
||||
uint8_t report[2] = {0};
|
||||
esp_err_t ret;
|
||||
|
||||
if (state) {
|
||||
report[0] = WII_REPORT_RUMBLE;
|
||||
report[1] = 0x1 | WII_LED4;
|
||||
} else {
|
||||
report[0] = WII_REPORT_LEDS;
|
||||
report[1] = wii_speed | WII_LED4;
|
||||
}
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot set rumble state, send error: %x", ret);
|
||||
}
|
||||
|
||||
/* enable the speaker on the Wiimote. this will clear the rumble ! */
|
||||
static void wii_speaker_state(const esp_bd_addr_t bda, bool state) {
|
||||
uint8_t report[2] = {0};
|
||||
esp_err_t ret;
|
||||
|
||||
configASSERT(bda != NULL);
|
||||
ESP_LOGV(TAG, "setting speaker state to %x", state);
|
||||
report[0] = WII_REPORT_SPEAKER_E;
|
||||
if (state)
|
||||
report[1] = 0x04;
|
||||
else
|
||||
report[1] = 0x0;
|
||||
ret = esp_bt_hid_host_send_data((uint8_t *)bda, report, 2);
|
||||
if (ret)
|
||||
ESP_LOGE(TAG, "cannot set speaker state, send error: %x", ret);
|
||||
}
|
||||
|
||||
/* display calibration data. do not save it for now since we are calibrating ourselves */
|
||||
void wii_show_calibration(uint8_t *data) {
|
||||
uint8_t i;
|
||||
uint8_t checksum;
|
||||
|
||||
configASSERT(data != NULL);
|
||||
|
||||
ESP_LOGD(TAG, "EEPROM read from 0x%02x%02x", data[3], data[4]);
|
||||
/* skip the header */
|
||||
data += 5;
|
||||
wii_calibration_0g.x = ((data[0] << 2) | ((data[3] >> 4) & 3));
|
||||
wii_calibration_0g.y = ((data[1] << 2) | ((data[3] >> 2) & 3));
|
||||
wii_calibration_0g.z = ((data[2] << 2) | (data[3] & 3));
|
||||
|
||||
wii_calibration_1g.x = (((data[4] << 2) | ((data[7] >> 4) & 3)) - wii_calibration_0g.x);
|
||||
wii_calibration_1g.y = (((data[5] << 2) | ((data[7] >> 2) & 3)) - wii_calibration_0g.y);
|
||||
wii_calibration_1g.z = (((data[6] << 2) | (data[7] & 3)) - wii_calibration_0g.z);
|
||||
|
||||
ESP_LOGD(TAG, "Wiimote calibration data: 0G (%i %i %i) 1G (%i %i %i)…",
|
||||
wii_calibration_0g.x, wii_calibration_0g.y, wii_calibration_0g.z,
|
||||
wii_calibration_1g.x, wii_calibration_1g.y, wii_calibration_1g.z);
|
||||
|
||||
checksum = 0;
|
||||
for (i = 0 ; i < 9 ; i++)
|
||||
checksum += data[i];
|
||||
checksum += 0x55;
|
||||
ESP_LOGD(TAG, "\t… checksum read: %02X, calculated: %02X", data[9], checksum);
|
||||
if (data[9] != checksum)
|
||||
xTaskNotify(t_red, LED_ONCE, eSetValueWithOverwrite);
|
||||
}
|
98
main/wii.h
Normal file
98
main/wii.h
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* wii.h
|
||||
* quack
|
||||
*
|
||||
* Created by Michel DEPEIGE on 11/08/2024.
|
||||
* Copyright (c) 2024 Michel DEPEIGE.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the Apache License, Version 2.0 (the "License");
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WII_H
|
||||
#define WII_H
|
||||
|
||||
/* prototypes */
|
||||
uint8_t *wii_generate_pin(esp_bt_pin_code_t pin);
|
||||
void wii_init(void);
|
||||
void wii_input(esp_hidh_dev_t *dev, uint16_t id, uint8_t *data, uint16_t length);
|
||||
bool wii_is_nintendo_bda(esp_bd_addr_t bda);
|
||||
bool wii_is_wiimote(uint16_t vid, uint16_t pid);
|
||||
void wii_open(const esp_bd_addr_t bda);
|
||||
void wii_show_calibration(uint8_t *data);
|
||||
|
||||
/* defines */
|
||||
#define WII_VID 0x057e
|
||||
#define WII_PID_OLD 0x0306
|
||||
#define WII_PID_NEW 0x0330
|
||||
|
||||
#define WII_PIN_STR "0x%02x%02x%02x%02x%02x%02x"
|
||||
#define WII_PIN_HEX(pin) pin[0], pin[1], pin[2], pin[3], pin[4], pin[5]
|
||||
|
||||
#define WII_THRESHOLD 0.1f
|
||||
#define WII_ACCELERATION -10.0f /* coonvert gravity to pixels */
|
||||
#define WII_INTERVAL (1000 / 60) /* pad update interval, about 60 Hz */
|
||||
#define WII_THROTTLE 20
|
||||
|
||||
/* core buttons. LX6 is wrong endian */
|
||||
#define WII_BUTTON_NONE 0x0000
|
||||
#define WII_BUTTON_LEFT 0x0001
|
||||
#define WII_BUTTON_RIGHT 0x0002
|
||||
#define WII_BUTTON_DOWN 0x0004
|
||||
#define WII_BUTTON_UP 0x0008
|
||||
#define WII_BUTTON_PLUS 0x0010
|
||||
#define WII_BUTTON_TWO 0x0100
|
||||
#define WII_BUTTON_ONE 0x0200
|
||||
#define WII_BUTTON_B 0x0400
|
||||
#define WII_BUTTON_A 0x0800
|
||||
#define WII_BUTTON_MINUS 0x1000
|
||||
#define WII_BUTTON_HOME 0x8000
|
||||
|
||||
/* LEDs + Rumble */
|
||||
#define WII_LED1 0x10
|
||||
#define WII_LED2 0x20
|
||||
#define WII_LED3 0x40
|
||||
#define WII_LED4 0x80
|
||||
|
||||
/* for quadrature speed feedback */
|
||||
#define WII_SPEED_NONE 0x00
|
||||
#define WII_SPEED_LOW WII_LED1
|
||||
#define WII_SPEED_MEDIUM WII_LED2
|
||||
#define WII_SPEED_HIGH WII_LED3
|
||||
|
||||
/* handled reports */
|
||||
#define WII_REPORT_RUMBLE 0x10
|
||||
#define WII_REPORT_LEDS 0x11
|
||||
#define WII_REPORT_MODE 0x12
|
||||
#define WII_REPORT_SPEAKER_E 0x14
|
||||
#define WII_REPORT_REQUEST 0x15
|
||||
#define WII_REPORT_WRITE_REG 0x16
|
||||
#define WII_REPORT_READ_REG 0x17
|
||||
#define WII_REPORT_SPEAKER_D 0x18
|
||||
#define WII_REPORT_SPEAKER_M 0x19
|
||||
#define WII_REPORT_STATUS 0x20
|
||||
#define WII_REPORT_READ_INPUT 0x21
|
||||
#define WII_REPORT_ACK 0x22
|
||||
#define WII_REPORT_CORE_ONLY 0x30
|
||||
#define WII_REPORT_CORE_ACCEL 0x31
|
||||
|
||||
#define WII_REPORT_INPUT WII_REPORT_ACK|WII_REPORT_CORE_ONLY
|
||||
|
||||
/* structures */
|
||||
typedef struct wii_accel_s {
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
uint16_t z;
|
||||
} wii_accel_t;
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user