├── .gitignore ├── ESP32Wiimote.cpp ├── ESP32Wiimote.h ├── LICENSE.md ├── README.md ├── TinyWiimote.cpp ├── TinyWiimote.h ├── examples └── ESP32WiimoteDemo │ └── ESP32WiimoteDemo.ino ├── library.properties └── remocon_led1_on.png /.gitignore: -------------------------------------------------------------------------------- 1 | ESP32Wiimote.ino 2 | -------------------------------------------------------------------------------- /ESP32Wiimote.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Daiki Yasuda 2 | // 3 | // This is licensed under 4 | // - Creative Commons Attribution-NonCommercial 3.0 Unported 5 | // - https://creativecommons.org/licenses/by-nc/3.0/ 6 | // - Or see LICENSE.md 7 | // 8 | // The short of it is... 9 | // You are free to: 10 | // Share — copy and redistribute the material in any medium or format 11 | // Adapt — remix, transform, and build upon the material 12 | // Under the following terms: 13 | // NonCommercial — You may not use the material for commercial purposes. 14 | 15 | #define CONFIG_CLASSIC_BT_ENABLED 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "nvs.h" 22 | #include "nvs_flash.h" 23 | #include "freertos/FreeRTOS.h" 24 | #include "freertos/task.h" 25 | #include "Arduino.h" 26 | #include "esp_bt.h" 27 | #include 28 | 29 | #include "time.h" 30 | #include "sys/time.h" 31 | 32 | #include "ESP32Wiimote.h" 33 | #include "TinyWiimote.h" 34 | 35 | #define WIIMOTE_VERBOSE 0 36 | 37 | #if WIIMOTE_VERBOSE 38 | #define VERBOSE_PRINT(...) Serial.printf(__VA_ARGS__) 39 | #define VERBOSE_PRINTLN(...) Serial.println(__VA_ARGS__) 40 | #else 41 | #define VERBOSE_PRINT(...) do {} while(0) 42 | #define VERBOSE_PRINTLN(...) do {} while(0) 43 | #endif 44 | 45 | #define RX_QUEUE_SIZE 32 46 | #define TX_QUEUE_SIZE 32 47 | xQueueHandle ESP32Wiimote::rxQueue = NULL; 48 | xQueueHandle ESP32Wiimote::txQueue = NULL; 49 | 50 | const TwHciInterface ESP32Wiimote::tinywii_hci_interface = { 51 | ESP32Wiimote::hciHostSendPacket 52 | }; 53 | 54 | esp_vhci_host_callback_t ESP32Wiimote::vhci_callback; 55 | 56 | ESP32Wiimote::ESP32Wiimote(void) 57 | { 58 | _pNunchukState = &_nunchukStateA; 59 | _pOldNunchukState = &_nunchukStateB; 60 | _nunStickThreshold = NUNCHUK_STICK_THRESHOLD * NUNCHUK_STICK_THRESHOLD; 61 | _filter = FILTER_NONE; 62 | } 63 | 64 | void ESP32Wiimote::notifyHostSendAvailable(void) { 65 | VERBOSE_PRINT("notifyHostSendAvailable\n"); 66 | if(!TinyWiimoteDeviceIsInited()){ 67 | TinyWiimoteResetDevice(); 68 | } 69 | } 70 | 71 | void ESP32Wiimote::createQueue(void) { 72 | txQueue = xQueueCreate(TX_QUEUE_SIZE, sizeof(queuedata_t*)); 73 | if (txQueue == NULL){ 74 | VERBOSE_PRINTLN("xQueueCreate(txQueue) failed"); 75 | return; 76 | } 77 | rxQueue = xQueueCreate(RX_QUEUE_SIZE, sizeof(queuedata_t*)); 78 | if (rxQueue == NULL){ 79 | VERBOSE_PRINTLN("xQueueCreate(rxQueue) failed"); 80 | return; 81 | } 82 | } 83 | 84 | void ESP32Wiimote::handleTxQueue(void) { 85 | if(uxQueueMessagesWaiting(txQueue)){ 86 | bool ok = esp_vhci_host_check_send_available(); 87 | VERBOSE_PRINT("esp_vhci_host_check_send_available=%d", ok); 88 | if(ok){ 89 | queuedata_t *queuedata = NULL; 90 | if(xQueueReceive(txQueue, &queuedata, 0) == pdTRUE){ 91 | esp_vhci_host_send_packet(queuedata->data, queuedata->len); 92 | VERBOSE_PRINT("SEND => %s", format2Hex(queuedata->data, queuedata->len)); 93 | free(queuedata); 94 | } 95 | } 96 | } 97 | } 98 | 99 | void ESP32Wiimote::handleRxQueue(void) { 100 | if(uxQueueMessagesWaiting(rxQueue)){ 101 | queuedata_t *queuedata = NULL; 102 | if(xQueueReceive(rxQueue, &queuedata, 0) == pdTRUE){ 103 | handleHciData(queuedata->data, queuedata->len); 104 | free(queuedata); 105 | } 106 | } 107 | } 108 | 109 | esp_err_t ESP32Wiimote::sendQueueData(xQueueHandle queue, uint8_t *data, size_t len) { 110 | VERBOSE_PRINTLN("sendQueueData"); 111 | if(!data || !len){ 112 | VERBOSE_PRINTLN("no data"); 113 | return ESP_OK; 114 | } 115 | queuedata_t * queuedata = (queuedata_t*)malloc(sizeof(queuedata_t) + len); 116 | if(!queuedata){ 117 | VERBOSE_PRINTLN("malloc failed"); 118 | return ESP_FAIL; 119 | } 120 | queuedata->len = len; 121 | memcpy(queuedata->data, data, len); 122 | if (xQueueSend(queue, &queuedata, portMAX_DELAY) != pdPASS) { 123 | VERBOSE_PRINTLN("xQueueSend failed"); 124 | free(queuedata); 125 | return ESP_FAIL; 126 | } 127 | return ESP_OK; 128 | } 129 | 130 | void ESP32Wiimote::hciHostSendPacket(uint8_t *data, size_t len) { 131 | sendQueueData(txQueue, data, len); 132 | } 133 | 134 | int ESP32Wiimote::notifyHostRecv(uint8_t *data, uint16_t len) { 135 | VERBOSE_PRINT("notifyHostRecv:"); 136 | for (int i = 0; i < len; i++) 137 | { 138 | VERBOSE_PRINT(" %02x", data[i]); 139 | } 140 | VERBOSE_PRINTLN(""); 141 | 142 | if(ESP_OK == sendQueueData(rxQueue, data, len)){ 143 | return ESP_OK; 144 | }else{ 145 | return ESP_FAIL; 146 | } 147 | } 148 | 149 | void ESP32Wiimote::init(void) 150 | { 151 | TinyWiimoteInit(tinywii_hci_interface); 152 | createQueue(); 153 | vhci_callback.notify_host_recv = notifyHostRecv; 154 | vhci_callback.notify_host_send_available = notifyHostSendAvailable; 155 | 156 | esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); 157 | if (!btStart()) { 158 | Serial.printf( "btStart() failed\n"); 159 | return; 160 | } 161 | 162 | esp_err_t ret; 163 | if ((ret = esp_vhci_host_register_callback(&vhci_callback)) != ESP_OK) { 164 | return; 165 | } 166 | } 167 | 168 | void ESP32Wiimote::task(void) 169 | { 170 | if(!btStarted()){ 171 | return; 172 | } 173 | handleTxQueue(); 174 | handleRxQueue(); 175 | } 176 | 177 | int ESP32Wiimote::available(void) 178 | { 179 | int stateIsAvailable = TinyWiimoteAvailable(); 180 | if (stateIsAvailable) { 181 | NunchukState *pTmpNunchuck; 182 | 183 | _gotData = TinyWiimoteRead(); 184 | 185 | // update old button state 186 | _oldButtonState = _buttonState; 187 | 188 | // update button state 189 | _buttonState = 0; 190 | _buttonState = (_gotData.data[TWII_OFFSET_BTNS1] << 8) | _gotData.data[TWII_OFFSET_BTNS2]; 191 | 192 | // update old nunchuck state(= exchange nunchuk state area) 193 | pTmpNunchuck = _pOldNunchukState; 194 | _pOldNunchukState = _pNunchukState; 195 | _pNunchukState = pTmpNunchuck; 196 | 197 | // update nunchuk state 198 | _pNunchukState->xStick = _gotData.data[TWII_OFFSET_EXTCTRL + 0]; 199 | _pNunchukState->yStick = _gotData.data[TWII_OFFSET_EXTCTRL + 1]; 200 | _pNunchukState->xAxis = _gotData.data[TWII_OFFSET_EXTCTRL + 2]; 201 | _pNunchukState->yAxis = _gotData.data[TWII_OFFSET_EXTCTRL + 3]; 202 | _pNunchukState->zAxis = _gotData.data[TWII_OFFSET_EXTCTRL + 4]; 203 | _pNunchukState->cBtn = ((_gotData.data[TWII_OFFSET_EXTCTRL + 5] & 0x02) >> 1) ^ 0x01; 204 | _pNunchukState->zBtn = (_gotData.data[TWII_OFFSET_EXTCTRL + 5] & 0x01) ^ 0x01; 205 | 206 | // check button change 207 | int buttonIsChanged = false; 208 | if (_filter & FILTER_REMOTE_BUTTON) { 209 | ; // ignore 210 | } 211 | else if (_buttonState != _oldButtonState) { 212 | buttonIsChanged = true; 213 | } 214 | 215 | // check nunchuk stick change 216 | int nunchukStickIsChanged = false; 217 | int nunXStickDelta = (int)(_pNunchukState->xStick) - _pOldNunchukState->xStick; 218 | int nunYStickDelta = (int)(_pNunchukState->yStick) - _pOldNunchukState->yStick; 219 | int nunStickDelta = (nunXStickDelta*nunXStickDelta + nunYStickDelta*nunYStickDelta) / 2; 220 | if (_filter & FILTER_NUNCHUK_STICK) { 221 | ; // ignore 222 | } 223 | else if (nunStickDelta >= _nunStickThreshold) { 224 | nunchukStickIsChanged = true; 225 | } 226 | 227 | // check nunchuk button change 228 | int nunchukButtonIsChanged = false; 229 | if (_filter & FILTER_NUNCHUK_BUTTON) { 230 | ; // ignore 231 | } 232 | else if ( 233 | (_pNunchukState->cBtn != _pOldNunchukState->cBtn) 234 | || (_pNunchukState->zBtn != _pOldNunchukState->zBtn) 235 | ) { 236 | nunchukButtonIsChanged = true; 237 | } 238 | 239 | // check accel change 240 | int accelIsChanged = false; 241 | if (_filter & FILTER_NUNCHUK_ACCEL) { 242 | ; // ignore 243 | } 244 | else { 245 | accelIsChanged = true; 246 | } 247 | 248 | stateIsAvailable = 249 | ( buttonIsChanged 250 | | nunchukStickIsChanged 251 | | nunchukButtonIsChanged 252 | | accelIsChanged 253 | ); 254 | 255 | } 256 | return stateIsAvailable; 257 | } 258 | 259 | uint16_t ESP32Wiimote::getButtonState(void) 260 | { 261 | return _buttonState; 262 | } 263 | 264 | NunchukState ESP32Wiimote::getNunchukState(void) 265 | { 266 | return *_pNunchukState; 267 | } 268 | 269 | void ESP32Wiimote::addFilter(int action, int filter) { 270 | if (action == ACTION_IGNORE) { 271 | _filter = _filter | filter; 272 | } 273 | } 274 | 275 | -------------------------------------------------------------------------------- /ESP32Wiimote.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Daiki Yasuda 2 | // 3 | // This is licensed under 4 | // - Creative Commons Attribution-NonCommercial 3.0 Unported 5 | // - https://creativecommons.org/licenses/by-nc/3.0/ 6 | // - Or see LICENSE.md 7 | // 8 | // The short of it is... 9 | // You are free to: 10 | // Share — copy and redistribute the material in any medium or format 11 | // Adapt — remix, transform, and build upon the material 12 | // Under the following terms: 13 | // NonCommercial — You may not use the material for commercial purposes. 14 | 15 | #ifndef __ESP32_WIIMOTE_H__ 16 | #define __ESP32_WIIMOTE_H__ 17 | 18 | #include "esp_bt.h" 19 | #include "TinyWiimote.h" 20 | 21 | typedef struct { 22 | uint8_t xStick; 23 | uint8_t yStick; 24 | uint8_t xAxis; 25 | uint8_t yAxis; 26 | uint8_t zAxis; 27 | uint8_t cBtn; 28 | uint8_t zBtn; 29 | } NunchukState; 30 | 31 | enum 32 | { 33 | FILTER_NONE = 0x0000, 34 | FILTER_REMOTE_BUTTON = 0x0001, 35 | FILTER_NUNCHUK_BUTTON = 0x0002, 36 | FILTER_NUNCHUK_STICK = 0x0004, 37 | FILTER_NUNCHUK_ACCEL = 0x0008, 38 | }; 39 | 40 | enum 41 | { 42 | ACTION_IGNORE, 43 | }; 44 | 45 | class ESP32Wiimote 46 | { 47 | public: 48 | enum 49 | { 50 | BUTTON_LEFT = 0x0800, 51 | BUTTON_RIGHT = 0x0400, 52 | BUTTON_UP = 0x0200, 53 | BUTTON_DOWN = 0x0100, 54 | BUTTON_A = 0x0008, 55 | BUTTON_B = 0x0004, 56 | BUTTON_PLUS = 0x1000, 57 | BUTTON_HOME = 0x0080, 58 | BUTTON_MINUS = 0x0010, 59 | BUTTON_ONE = 0x0002, 60 | BUTTON_TWO = 0x0001 61 | }; 62 | 63 | const int NUNCHUK_STICK_THRESHOLD = 2; 64 | 65 | ESP32Wiimote(void); 66 | 67 | void init(void); 68 | void task(void); 69 | int available(void); 70 | uint16_t getButtonState(void); 71 | NunchukState getNunchukState(void); 72 | void addFilter(int action, int filter); 73 | 74 | private: 75 | 76 | typedef struct { 77 | size_t len; 78 | uint8_t data[]; 79 | } queuedata_t; 80 | 81 | TinyWiimoteData _gotData; 82 | 83 | uint16_t _buttonState; 84 | uint16_t _oldButtonState; 85 | 86 | NunchukState *_pNunchukState; 87 | NunchukState *_pOldNunchukState; 88 | 89 | NunchukState _nunchukStateA; 90 | NunchukState _nunchukStateB; 91 | 92 | int _nunStickThreshold; 93 | 94 | int _filter; 95 | 96 | static const TwHciInterface tinywii_hci_interface; 97 | static esp_vhci_host_callback_t vhci_callback; 98 | static xQueueHandle txQueue; 99 | static xQueueHandle rxQueue; 100 | 101 | static void createQueue(void); 102 | static void handleTxQueue(void); 103 | static void handleRxQueue(void); 104 | static esp_err_t sendQueueData(xQueueHandle queue, uint8_t *data, size_t len); 105 | static void notifyHostSendAvailable(void); 106 | static int notifyHostRecv(uint8_t *data, uint16_t len); 107 | static void hciHostSendPacket(uint8_t *data, size_t len); 108 | 109 | }; 110 | 111 | #endif // __ESP32_WIIMOTE_H__ 112 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | Attribution-NonCommercial 3.0 Unported 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR 10 | DAMAGES RESULTING FROM ITS USE. 11 | 12 | License 13 | 14 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE 15 | COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY 16 | COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS 17 | AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. 18 | 19 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE 20 | TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY 21 | BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS 22 | CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND 23 | CONDITIONS. 24 | 25 | 1. Definitions 26 | 27 | a. "Adaptation" means a work based upon the Work, or upon the Work and 28 | other pre-existing works, such as a translation, adaptation, 29 | derivative work, arrangement of music or other alterations of a 30 | literary or artistic work, or phonogram or performance and includes 31 | cinematographic adaptations or any other form in which the Work may be 32 | recast, transformed, or adapted including in any form recognizably 33 | derived from the original, except that a work that constitutes a 34 | Collection will not be considered an Adaptation for the purpose of 35 | this License. For the avoidance of doubt, where the Work is a musical 36 | work, performance or phonogram, the synchronization of the Work in 37 | timed-relation with a moving image ("synching") will be considered an 38 | Adaptation for the purpose of this License. 39 | b. "Collection" means a collection of literary or artistic works, such as 40 | encyclopedias and anthologies, or performances, phonograms or 41 | broadcasts, or other works or subject matter other than works listed 42 | in Section 1(f) below, which, by reason of the selection and 43 | arrangement of their contents, constitute intellectual creations, in 44 | which the Work is included in its entirety in unmodified form along 45 | with one or more other contributions, each constituting separate and 46 | independent works in themselves, which together are assembled into a 47 | collective whole. A work that constitutes a Collection will not be 48 | considered an Adaptation (as defined above) for the purposes of this 49 | License. 50 | c. "Distribute" means to make available to the public the original and 51 | copies of the Work or Adaptation, as appropriate, through sale or 52 | other transfer of ownership. 53 | d. "Licensor" means the individual, individuals, entity or entities that 54 | offer(s) the Work under the terms of this License. 55 | e. "Original Author" means, in the case of a literary or artistic work, 56 | the individual, individuals, entity or entities who created the Work 57 | or if no individual or entity can be identified, the publisher; and in 58 | addition (i) in the case of a performance the actors, singers, 59 | musicians, dancers, and other persons who act, sing, deliver, declaim, 60 | play in, interpret or otherwise perform literary or artistic works or 61 | expressions of folklore; (ii) in the case of a phonogram the producer 62 | being the person or legal entity who first fixes the sounds of a 63 | performance or other sounds; and, (iii) in the case of broadcasts, the 64 | organization that transmits the broadcast. 65 | f. "Work" means the literary and/or artistic work offered under the terms 66 | of this License including without limitation any production in the 67 | literary, scientific and artistic domain, whatever may be the mode or 68 | form of its expression including digital form, such as a book, 69 | pamphlet and other writing; a lecture, address, sermon or other work 70 | of the same nature; a dramatic or dramatico-musical work; a 71 | choreographic work or entertainment in dumb show; a musical 72 | composition with or without words; a cinematographic work to which are 73 | assimilated works expressed by a process analogous to cinematography; 74 | a work of drawing, painting, architecture, sculpture, engraving or 75 | lithography; a photographic work to which are assimilated works 76 | expressed by a process analogous to photography; a work of applied 77 | art; an illustration, map, plan, sketch or three-dimensional work 78 | relative to geography, topography, architecture or science; a 79 | performance; a broadcast; a phonogram; a compilation of data to the 80 | extent it is protected as a copyrightable work; or a work performed by 81 | a variety or circus performer to the extent it is not otherwise 82 | considered a literary or artistic work. 83 | g. "You" means an individual or entity exercising rights under this 84 | License who has not previously violated the terms of this License with 85 | respect to the Work, or who has received express permission from the 86 | Licensor to exercise rights under this License despite a previous 87 | violation. 88 | h. "Publicly Perform" means to perform public recitations of the Work and 89 | to communicate to the public those public recitations, by any means or 90 | process, including by wire or wireless means or public digital 91 | performances; to make available to the public Works in such a way that 92 | members of the public may access these Works from a place and at a 93 | place individually chosen by them; to perform the Work to the public 94 | by any means or process and the communication to the public of the 95 | performances of the Work, including by public digital performance; to 96 | broadcast and rebroadcast the Work by any means including signs, 97 | sounds or images. 98 | i. "Reproduce" means to make copies of the Work by any means including 99 | without limitation by sound or visual recordings and the right of 100 | fixation and reproducing fixations of the Work, including storage of a 101 | protected performance or phonogram in digital form or other electronic 102 | medium. 103 | 104 | 2. Fair Dealing Rights. Nothing in this License is intended to reduce, 105 | limit, or restrict any uses free from copyright or rights arising from 106 | limitations or exceptions that are provided for in connection with the 107 | copyright protection under copyright law or other applicable laws. 108 | 109 | 3. License Grant. Subject to the terms and conditions of this License, 110 | Licensor hereby grants You a worldwide, royalty-free, non-exclusive, 111 | perpetual (for the duration of the applicable copyright) license to 112 | exercise the rights in the Work as stated below: 113 | 114 | a. to Reproduce the Work, to incorporate the Work into one or more 115 | Collections, and to Reproduce the Work as incorporated in the 116 | Collections; 117 | b. to create and Reproduce Adaptations provided that any such Adaptation, 118 | including any translation in any medium, takes reasonable steps to 119 | clearly label, demarcate or otherwise identify that changes were made 120 | to the original Work. For example, a translation could be marked "The 121 | original work was translated from English to Spanish," or a 122 | modification could indicate "The original work has been modified."; 123 | c. to Distribute and Publicly Perform the Work including as incorporated 124 | in Collections; and, 125 | d. to Distribute and Publicly Perform Adaptations. 126 | 127 | The above rights may be exercised in all media and formats whether now 128 | known or hereafter devised. The above rights include the right to make 129 | such modifications as are technically necessary to exercise the rights in 130 | other media and formats. Subject to Section 8(f), all rights not expressly 131 | granted by Licensor are hereby reserved, including but not limited to the 132 | rights set forth in Section 4(d). 133 | 134 | 4. Restrictions. The license granted in Section 3 above is expressly made 135 | subject to and limited by the following restrictions: 136 | 137 | a. You may Distribute or Publicly Perform the Work only under the terms 138 | of this License. You must include a copy of, or the Uniform Resource 139 | Identifier (URI) for, this License with every copy of the Work You 140 | Distribute or Publicly Perform. You may not offer or impose any terms 141 | on the Work that restrict the terms of this License or the ability of 142 | the recipient of the Work to exercise the rights granted to that 143 | recipient under the terms of the License. You may not sublicense the 144 | Work. You must keep intact all notices that refer to this License and 145 | to the disclaimer of warranties with every copy of the Work You 146 | Distribute or Publicly Perform. When You Distribute or Publicly 147 | Perform the Work, You may not impose any effective technological 148 | measures on the Work that restrict the ability of a recipient of the 149 | Work from You to exercise the rights granted to that recipient under 150 | the terms of the License. This Section 4(a) applies to the Work as 151 | incorporated in a Collection, but this does not require the Collection 152 | apart from the Work itself to be made subject to the terms of this 153 | License. If You create a Collection, upon notice from any Licensor You 154 | must, to the extent practicable, remove from the Collection any credit 155 | as required by Section 4(c), as requested. If You create an 156 | Adaptation, upon notice from any Licensor You must, to the extent 157 | practicable, remove from the Adaptation any credit as required by 158 | Section 4(c), as requested. 159 | b. You may not exercise any of the rights granted to You in Section 3 160 | above in any manner that is primarily intended for or directed toward 161 | commercial advantage or private monetary compensation. The exchange of 162 | the Work for other copyrighted works by means of digital file-sharing 163 | or otherwise shall not be considered to be intended for or directed 164 | toward commercial advantage or private monetary compensation, provided 165 | there is no payment of any monetary compensation in connection with 166 | the exchange of copyrighted works. 167 | c. If You Distribute, or Publicly Perform the Work or any Adaptations or 168 | Collections, You must, unless a request has been made pursuant to 169 | Section 4(a), keep intact all copyright notices for the Work and 170 | provide, reasonable to the medium or means You are utilizing: (i) the 171 | name of the Original Author (or pseudonym, if applicable) if supplied, 172 | and/or if the Original Author and/or Licensor designate another party 173 | or parties (e.g., a sponsor institute, publishing entity, journal) for 174 | attribution ("Attribution Parties") in Licensor's copyright notice, 175 | terms of service or by other reasonable means, the name of such party 176 | or parties; (ii) the title of the Work if supplied; (iii) to the 177 | extent reasonably practicable, the URI, if any, that Licensor 178 | specifies to be associated with the Work, unless such URI does not 179 | refer to the copyright notice or licensing information for the Work; 180 | and, (iv) consistent with Section 3(b), in the case of an Adaptation, 181 | a credit identifying the use of the Work in the Adaptation (e.g., 182 | "French translation of the Work by Original Author," or "Screenplay 183 | based on original Work by Original Author"). The credit required by 184 | this Section 4(c) may be implemented in any reasonable manner; 185 | provided, however, that in the case of a Adaptation or Collection, at 186 | a minimum such credit will appear, if a credit for all contributing 187 | authors of the Adaptation or Collection appears, then as part of these 188 | credits and in a manner at least as prominent as the credits for the 189 | other contributing authors. For the avoidance of doubt, You may only 190 | use the credit required by this Section for the purpose of attribution 191 | in the manner set out above and, by exercising Your rights under this 192 | License, You may not implicitly or explicitly assert or imply any 193 | connection with, sponsorship or endorsement by the Original Author, 194 | Licensor and/or Attribution Parties, as appropriate, of You or Your 195 | use of the Work, without the separate, express prior written 196 | permission of the Original Author, Licensor and/or Attribution 197 | Parties. 198 | d. For the avoidance of doubt: 199 | 200 | i. Non-waivable Compulsory License Schemes. In those jurisdictions in 201 | which the right to collect royalties through any statutory or 202 | compulsory licensing scheme cannot be waived, the Licensor 203 | reserves the exclusive right to collect such royalties for any 204 | exercise by You of the rights granted under this License; 205 | ii. Waivable Compulsory License Schemes. In those jurisdictions in 206 | which the right to collect royalties through any statutory or 207 | compulsory licensing scheme can be waived, the Licensor reserves 208 | the exclusive right to collect such royalties for any exercise by 209 | You of the rights granted under this License if Your exercise of 210 | such rights is for a purpose or use which is otherwise than 211 | noncommercial as permitted under Section 4(b) and otherwise waives 212 | the right to collect royalties through any statutory or compulsory 213 | licensing scheme; and, 214 | iii. Voluntary License Schemes. The Licensor reserves the right to 215 | collect royalties, whether individually or, in the event that the 216 | Licensor is a member of a collecting society that administers 217 | voluntary licensing schemes, via that society, from any exercise 218 | by You of the rights granted under this License that is for a 219 | purpose or use which is otherwise than noncommercial as permitted 220 | under Section 4(c). 221 | e. Except as otherwise agreed in writing by the Licensor or as may be 222 | otherwise permitted by applicable law, if You Reproduce, Distribute or 223 | Publicly Perform the Work either by itself or as part of any 224 | Adaptations or Collections, You must not distort, mutilate, modify or 225 | take other derogatory action in relation to the Work which would be 226 | prejudicial to the Original Author's honor or reputation. Licensor 227 | agrees that in those jurisdictions (e.g. Japan), in which any exercise 228 | of the right granted in Section 3(b) of this License (the right to 229 | make Adaptations) would be deemed to be a distortion, mutilation, 230 | modification or other derogatory action prejudicial to the Original 231 | Author's honor and reputation, the Licensor will waive or not assert, 232 | as appropriate, this Section, to the fullest extent permitted by the 233 | applicable national law, to enable You to reasonably exercise Your 234 | right under Section 3(b) of this License (right to make Adaptations) 235 | but not otherwise. 236 | 237 | 5. Representations, Warranties and Disclaimer 238 | 239 | UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR 240 | OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY 241 | KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, 242 | INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, 243 | FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF 244 | LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, 245 | WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION 246 | OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 247 | 248 | 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE 249 | LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR 250 | ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES 251 | ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS 252 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 253 | 254 | 7. Termination 255 | 256 | a. This License and the rights granted hereunder will terminate 257 | automatically upon any breach by You of the terms of this License. 258 | Individuals or entities who have received Adaptations or Collections 259 | from You under this License, however, will not have their licenses 260 | terminated provided such individuals or entities remain in full 261 | compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will 262 | survive any termination of this License. 263 | b. Subject to the above terms and conditions, the license granted here is 264 | perpetual (for the duration of the applicable copyright in the Work). 265 | Notwithstanding the above, Licensor reserves the right to release the 266 | Work under different license terms or to stop distributing the Work at 267 | any time; provided, however that any such election will not serve to 268 | withdraw this License (or any other license that has been, or is 269 | required to be, granted under the terms of this License), and this 270 | License will continue in full force and effect unless terminated as 271 | stated above. 272 | 273 | 8. Miscellaneous 274 | 275 | a. Each time You Distribute or Publicly Perform the Work or a Collection, 276 | the Licensor offers to the recipient a license to the Work on the same 277 | terms and conditions as the license granted to You under this License. 278 | b. Each time You Distribute or Publicly Perform an Adaptation, Licensor 279 | offers to the recipient a license to the original Work on the same 280 | terms and conditions as the license granted to You under this License. 281 | c. If any provision of this License is invalid or unenforceable under 282 | applicable law, it shall not affect the validity or enforceability of 283 | the remainder of the terms of this License, and without further action 284 | by the parties to this agreement, such provision shall be reformed to 285 | the minimum extent necessary to make such provision valid and 286 | enforceable. 287 | d. No term or provision of this License shall be deemed waived and no 288 | breach consented to unless such waiver or consent shall be in writing 289 | and signed by the party to be charged with such waiver or consent. 290 | e. This License constitutes the entire agreement between the parties with 291 | respect to the Work licensed here. There are no understandings, 292 | agreements or representations with respect to the Work not specified 293 | here. Licensor shall not be bound by any additional provisions that 294 | may appear in any communication from You. This License may not be 295 | modified without the mutual written agreement of the Licensor and You. 296 | f. The rights granted under, and the subject matter referenced, in this 297 | License were drafted utilizing the terminology of the Berne Convention 298 | for the Protection of Literary and Artistic Works (as amended on 299 | September 28, 1979), the Rome Convention of 1961, the WIPO Copyright 300 | Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 301 | and the Universal Copyright Convention (as revised on July 24, 1971). 302 | These rights and subject matter take effect in the relevant 303 | jurisdiction in which the License terms are sought to be enforced 304 | according to the corresponding provisions of the implementation of 305 | those treaty provisions in the applicable national law. If the 306 | standard suite of rights granted under applicable copyright law 307 | includes additional rights not granted under this License, such 308 | additional rights are deemed to be included in the License; this 309 | License is not intended to restrict the license of any rights under 310 | applicable law. 311 | 312 | 313 | Creative Commons Notice 314 | 315 | Creative Commons is not a party to this License, and makes no warranty 316 | whatsoever in connection with the Work. Creative Commons will not be 317 | liable to You or any party on any legal theory for any damages 318 | whatsoever, including without limitation any general, special, 319 | incidental or consequential damages arising in connection to this 320 | license. Notwithstanding the foregoing two (2) sentences, if Creative 321 | Commons has expressly identified itself as the Licensor hereunder, it 322 | shall have all rights and obligations of Licensor. 323 | 324 | Except for the limited purpose of indicating to the public that the 325 | Work is licensed under the CCPL, Creative Commons does not authorize 326 | the use by either party of the trademark "Creative Commons" or any 327 | related trademark or logo of Creative Commons without the prior 328 | written consent of Creative Commons. Any permitted use will be in 329 | compliance with Creative Commons' then-current trademark usage 330 | guidelines, as may be published on its website or otherwise made 331 | available upon request from time to time. For the avoidance of doubt, 332 | this trademark restriction does not form part of the License. 333 | 334 | Creative Commons may be contacted at https://creativecommons.org/. 335 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino-ESP32Wiimote 2 | 3 | ESP32Wiimote is a Arduino library that connects with a Wii remote. 4 | 5 | ## Requirement 6 | 7 | - [ESP32 dev board](https://www.switch-science.com/catalog/3210/) 8 | - Arduino IDE (Version: 1.8.5) 9 | - Wii Remote (RVL-CNT-01) 10 | 11 | ## Installation 12 | 1. Download the zip file. 13 | 2. Move the zip file to your libraries directory. 14 | 3. In the Arduino IDE, navigate to Sketch > Include Library > Add .ZIP Library. 15 | 4. Select the zip file. 16 | 17 | ## Examples 18 | 19 | ### Basic Example 20 | 21 | ```ESP32WiimoteDemo.ino.cpp 22 | #include "ESP32Wiimote.h" 23 | 24 | ESP32Wiimote wiimote; 25 | 26 | void setup() 27 | { 28 | Serial.begin(115200); 29 | wiimote.init(); 30 | } 31 | 32 | void loop() 33 | { 34 | wiimote.task(); 35 | if (wiimote.available() > 0) { 36 | uint16_t button = wiimote.getButtonState(); 37 | Serial.printf("%04x\n", button); 38 | if (button == ESP32Wiimote::BUTTON_A) { 39 | Serial.println("A button"); 40 | } 41 | } 42 | delay(10); 43 | } 44 | 45 | 46 | ``` 47 | ### Example With Nunchuck 48 | 49 | ```ESP32WiimoteDemo.ino.cpp 50 | #include "ESP32Wiimote.h" 51 | 52 | ESP32Wiimote wiimote; 53 | 54 | void setup() 55 | { 56 | Serial.begin(115200); 57 | wiimote.init(); 58 | wiimote.addFilter(ACTION_IGNORE, FILTER_NUNCHUK_ACCEL); 59 | } 60 | 61 | void loop() 62 | { 63 | wiimote.task(); 64 | if (wiimote.available() > 0) { 65 | uint16_t button = wiimote.getButtonState(); 66 | Serial.printf("%04x\n", button); 67 | 68 | NunchukState nunchuk = wiimote.getNunchukState(); 69 | Serial.printf("nunchuk:"); 70 | Serial.printf(" X-Stick: %d", nunchuk.xStick); 71 | Serial.printf(" Y-Stick: %d", nunchuk.yStick); 72 | Serial.printf(" X-Axis: %d", nunchuk.xAxis); 73 | Serial.printf(" Y-Axis: %d", nunchuk.yAxis); 74 | Serial.printf(" Z-Axis: %d", nunchuk.zAxis); 75 | Serial.printf(" C-Button: %02x", nunchuk.cBtn); 76 | Serial.printf(" Z-Button: %02x", nunchuk.zBtn); 77 | Serial.printf("\n"); 78 | } 79 | delay(10); 80 | } 81 | 82 | ``` 83 | 84 | - Caution: Nunchuck keeps outputting a lot of data for acceleration sensing 85 | - You can Ignore changes with 'add filter(ACTION_IGNORE,...)' 86 | 87 | #### Button Definition 88 | 'button' is expressed as OR of bits: 89 | 90 | ``` 91 | BUTTON_LEFT = 0x0800, 92 | BUTTON_RIGHT = 0x0400, 93 | BUTTON_UP = 0x0200, 94 | BUTTON_DOWN = 0x0100, 95 | BUTTON_A = 0x0008, 96 | BUTTON_B = 0x0004, 97 | BUTTON_PLUS = 0x1000, 98 | BUTTON_HOME = 0x0080, 99 | BUTTON_MINUS = 0x0010, 100 | BUTTON_ONE = 0x0002, 101 | BUTTON_TWO = 0x0001 102 | ``` 103 | ## Usage 104 | 105 | 1. To connect, press the 1 and 2 buttons on Wii Remote 106 | 107 | 1. The LED1 will be on when they have finished connecting 108 | 109 | 110 | ## Licence 111 | 112 | see [LICENSE.md](./LICENSE.md) 113 | -------------------------------------------------------------------------------- /TinyWiimote.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Daiki Yasuda 2 | // 3 | // This is licensed under 4 | // - Creative Commons Attribution-NonCommercial 3.0 Unported 5 | // - https://creativecommons.org/licenses/by-nc/3.0/ 6 | // - Or see LICENSE.md 7 | // 8 | // The short of it is... 9 | // You are free to: 10 | // Share — copy and redistribute the material in any medium or format 11 | // Adapt — remix, transform, and build upon the material 12 | // Under the following terms: 13 | // NonCommercial — You may not use the material for commercial purposes. 14 | 15 | #define CONFIG_CLASSIC_BT_ENABLED 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "time.h" 23 | #include "sys/time.h" 24 | 25 | #include "TinyWiimote.h" 26 | 27 | #define WIIMOTE_VERBOSE 0 28 | 29 | #if WIIMOTE_VERBOSE 30 | #include // for Arduino 31 | #define VERBOSE_PRINT(...) Serial.printf(__VA_ARGS__) 32 | #define VERBOSE_PRINTLN(...) Serial.println(__VA_ARGS__) 33 | #else 34 | #define VERBOSE_PRINT(...) do {} while(0) 35 | #define VERBOSE_PRINTLN(...) do {} while(0) 36 | #endif 37 | 38 | #define HCI_H4_CMD_PREAMBLE_SIZE (4) 39 | #define HCI_H4_ACL_PREAMBLE_SIZE (5) 40 | 41 | #define BD_ADDR_LEN (6) 42 | struct bd_addr_t { 43 | uint8_t addr[BD_ADDR_LEN]; 44 | }; 45 | 46 | #define UINT16_TO_STREAM(p, u16) {*(p)++ = (uint8_t)(u16); *(p)++ = (uint8_t)((u16) >> 8);} 47 | #define UINT8_TO_STREAM(p, u8) {*(p)++ = (uint8_t)(u8);} 48 | #define BDADDR_TO_STREAM(p, a) {int ijk; for (ijk = 0; ijk < BD_ADDR_LEN; ijk++) *(p)++ = (uint8_t) a[BD_ADDR_LEN - 1 - ijk];} 49 | #define STREAM_TO_BDADDR(a, p) {int ijk; for (ijk = 0; ijk < BD_ADDR_LEN; ijk++) a[BD_ADDR_LEN - 1 - ijk] = (p)[ijk];} 50 | #define ARRAY_TO_STREAM(p, a, len) {int ijk; for (ijk = 0; ijk < len; ijk++) *(p)++ = (uint8_t) a[ijk];} 51 | 52 | enum { 53 | H4_TYPE_COMMAND = 1, 54 | H4_TYPE_ACL = 2, 55 | H4_TYPE_SCO = 3, 56 | H4_TYPE_EVENT = 4 57 | }; 58 | 59 | // L2CAP 60 | #define L2CAP_CONNECT_RES 0x03 61 | #define L2CAP_CONFIG_RES 0x05 62 | #define L2CAP_CONFIG_REQ 0x04 63 | 64 | // BTCODE 65 | #define BTCODE_HID 0xA1 66 | 67 | // HCI Events 68 | #define HCI_INQUIRY_COMP_EVT 0x01 69 | #define HCI_INQUIRY_RESULT_EVT 0x02 70 | #define HCI_CONNECTION_COMP_EVT 0x03 71 | #define HCI_DISCONNECTION_COMP_EVT 0x05 72 | #define HCI_RMT_NAME_REQUEST_COMP_EVT 0x07 73 | #define HCI_QOS_SETUP_COMP_EVT 0x0D 74 | #define HCI_COMMAND_COMPLETE_EVT 0x0E 75 | #define HCI_COMMAND_STATUS_EVT 0x0F 76 | #define HCI_NUM_COMPL_DATA_PKTS_EVT 0x13 77 | 78 | // HCI Command opcode group field(OGF) & Opcode Command Field (OCF) 79 | // refer : http://software-dl.ti.com/lprf/simplelink_cc26x2_sdk-1.60/docs/ble5stack/vendor_specific_guide/BLE_Vendor_Specific_HCI_Guide/hci_interface.html 80 | 81 | // Opcode Group Field (OGF) codes 82 | #define HCI_OGF_LINK_CONTROL 0x01 // Link control group 83 | #define HCI_OGF_CONTROL_BASEBAND 0x03 // Host Controller & Baseband group 84 | #define HCI_OGF_INFORMATIONAL_PARAMETERS 0x04 // Information parameters group 85 | 86 | // Host controller & baseband commands 87 | #define HCI_OCF_RESET 0x0003 88 | #define HCI_OCF_CHANGE_LOCAL_NAME 0x0013 89 | #define HCI_OCF_WRITE_CLASS_OF_DEVICE 0x0024 90 | #define HCI_OCF_WRITE_SCAN_ENABLE 0x001A 91 | 92 | // Informational parameter commands 93 | #define HCI_OCF_READ_BD_ADDR 0x0009 94 | 95 | // Link control commands 96 | #define HCI_OCF_INQUIRY 0x0001 97 | #define HCI_OCF_INQUIRY_CANCEL 0x0002 98 | #define HCI_OCF_CREATE_CONNECTION 0x0005 99 | #define HCI_OCF_REMOTE_NAME_REQUEST 0x0019 100 | 101 | // HCI Command opcodes(OGF + OCF) 102 | #define HCI_OPCODE_RESET (HCI_OCF_RESET | (HCI_OGF_CONTROL_BASEBAND << 10)) 103 | #define HCI_OPCODE_WRITE_LOCAL_NAME (HCI_OCF_CHANGE_LOCAL_NAME | (HCI_OGF_CONTROL_BASEBAND << 10)) 104 | #define HCI_OPCODE_WRITE_CLASS_OF_DEVICE (HCI_OCF_WRITE_CLASS_OF_DEVICE | (HCI_OGF_CONTROL_BASEBAND << 10)) 105 | #define HCI_OPCODE_WRITE_SCAN_ENABLE (HCI_OCF_WRITE_SCAN_ENABLE | (HCI_OGF_CONTROL_BASEBAND << 10)) 106 | #define HCI_OPCODE_READ_BD_ADDR (HCI_OCF_READ_BD_ADDR | (HCI_OGF_INFORMATIONAL_PARAMETERS << 10)) 107 | #define HCI_OPCODE_INQUIRY (HCI_OCF_INQUIRY | (HCI_OGF_LINK_CONTROL << 10)) 108 | #define HCI_OPCODE_INQUIRY_CANCEL (HCI_OCF_INQUIRY_CANCEL | (HCI_OGF_LINK_CONTROL << 10)) 109 | #define HCI_OPCODE_CREATE_CONNECTION (HCI_OCF_CREATE_CONNECTION | (HCI_OGF_LINK_CONTROL << 10)) 110 | #define HCI_OPCODE_REMOTE_NAME_REQUEST (HCI_OCF_REMOTE_NAME_REQUEST | (HCI_OGF_LINK_CONTROL << 10)) 111 | 112 | #define HCIC_PARAM_SIZE_WRITE_LOCAL_NAME (248) 113 | #define HCIC_PARAM_SIZE_WRITE_CLASS_OF_DEVICE (3) 114 | #define HCIC_PARAM_SIZE_WRITE_SCAN_ENABLE (1) 115 | #define HCIC_PARAM_SIZE_CREATE_CONNECTION (13) 116 | #define HCIC_PARAM_SIZE_REMOTE_NAME_REQUEST (10) 117 | #define HCIC_PARAM_SIZE_WRITE_INQUIRY_CANCEL (0) 118 | #define HCIC_PARAM_SIZE_WRITE_INQUIRY (5) 119 | 120 | static bool deviceInited = false; 121 | static bool wiimoteConnected = false; 122 | 123 | /** 124 | * Command Maker 125 | */ 126 | static uint16_t make_cmd_reset(uint8_t *buf) 127 | { 128 | UINT8_TO_STREAM (buf, H4_TYPE_COMMAND); 129 | UINT16_TO_STREAM (buf, HCI_OPCODE_RESET); 130 | UINT8_TO_STREAM (buf, 0); 131 | return HCI_H4_CMD_PREAMBLE_SIZE; 132 | } 133 | 134 | static uint16_t make_cmd_read_bd_addr(uint8_t *buf) 135 | { 136 | UINT8_TO_STREAM (buf, H4_TYPE_COMMAND); 137 | UINT16_TO_STREAM (buf, HCI_OPCODE_READ_BD_ADDR); 138 | UINT8_TO_STREAM (buf, 0); 139 | return HCI_H4_CMD_PREAMBLE_SIZE; 140 | } 141 | 142 | static uint16_t make_cmd_write_local_name(uint8_t *buf, uint8_t* name, uint8_t len) 143 | { 144 | UINT8_TO_STREAM (buf, H4_TYPE_COMMAND); 145 | UINT16_TO_STREAM (buf, HCI_OPCODE_WRITE_LOCAL_NAME); 146 | UINT8_TO_STREAM (buf, HCIC_PARAM_SIZE_WRITE_LOCAL_NAME); 147 | ARRAY_TO_STREAM(buf, name, len); 148 | for(uint8_t i=len; i> 8) & 0xFF)); // lap 0x8B 183 | UINT8_TO_STREAM (buf, (uint8_t)((lap>>16) & 0xFF)); // lap 0x9E 184 | UINT8_TO_STREAM (buf, len); 185 | UINT8_TO_STREAM (buf, num); 186 | return HCI_H4_CMD_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_INQUIRY; 187 | } 188 | 189 | static uint16_t make_cmd_inquiry_cancel(uint8_t *buf) 190 | { 191 | UINT8_TO_STREAM (buf, H4_TYPE_COMMAND); 192 | UINT16_TO_STREAM (buf, HCI_OPCODE_INQUIRY_CANCEL); 193 | UINT8_TO_STREAM (buf, HCIC_PARAM_SIZE_WRITE_INQUIRY_CANCEL); 194 | 195 | return HCI_H4_CMD_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_INQUIRY_CANCEL; 196 | } 197 | 198 | static uint16_t make_cmd_remote_name_request(uint8_t *buf, struct bd_addr_t bdAddr, uint8_t psrm, uint16_t clkofs) 199 | { 200 | UINT8_TO_STREAM (buf, H4_TYPE_COMMAND); 201 | UINT16_TO_STREAM (buf, HCI_OPCODE_REMOTE_NAME_REQUEST); 202 | UINT8_TO_STREAM (buf, HCIC_PARAM_SIZE_REMOTE_NAME_REQUEST); 203 | 204 | BDADDR_TO_STREAM (buf, bdAddr.addr); 205 | UINT8_TO_STREAM (buf, psrm); // Page Scan Repetition Mode 206 | UINT8_TO_STREAM (buf, 0); // Reserved 207 | UINT16_TO_STREAM (buf, clkofs); // Clock Offset 208 | return HCI_H4_CMD_PREAMBLE_SIZE + HCIC_PARAM_SIZE_REMOTE_NAME_REQUEST; 209 | } 210 | 211 | static uint16_t make_cmd_create_connection(uint8_t *buf, struct bd_addr_t bdAddr, uint16_t pt, uint8_t psrm, uint16_t clkofs, uint8_t ars) 212 | { 213 | UINT8_TO_STREAM (buf, H4_TYPE_COMMAND); 214 | UINT16_TO_STREAM (buf, HCI_OPCODE_CREATE_CONNECTION); 215 | UINT8_TO_STREAM (buf, HCIC_PARAM_SIZE_CREATE_CONNECTION); 216 | 217 | BDADDR_TO_STREAM (buf, bdAddr.addr); 218 | UINT16_TO_STREAM (buf, pt); // Packet Type 219 | UINT8_TO_STREAM (buf, psrm); // Page Scan Repetition Mode 220 | UINT8_TO_STREAM (buf, 0); // Reserved 221 | UINT16_TO_STREAM (buf, clkofs); // Clock Offset 222 | UINT8_TO_STREAM (buf, ars); // Allow Role Switch 223 | return HCI_H4_CMD_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CREATE_CONNECTION; 224 | } 225 | 226 | #define L2CAP_HEADER_LEN (4) //Length + Channel ID 227 | 228 | static uint16_t make_l2cap_packet(uint8_t *buf, uint16_t channelID, uint8_t *data, uint16_t len) { 229 | UINT16_TO_STREAM (buf, len); 230 | UINT16_TO_STREAM (buf, channelID); // 0x0001=Signaling channel 231 | ARRAY_TO_STREAM (buf, data, len); 232 | return L2CAP_HEADER_LEN + len; 233 | } 234 | 235 | static uint16_t make_acl_l2cap_packet(uint8_t *buf, uint16_t ch, uint8_t pbf, uint8_t bf, uint16_t channelID, uint8_t *data, uint8_t len) { 236 | uint8_t* l2cap_buf = buf + HCI_H4_ACL_PREAMBLE_SIZE; 237 | uint16_t l2capLen = make_l2cap_packet(l2cap_buf, channelID, data, len); 238 | 239 | UINT8_TO_STREAM (buf, H4_TYPE_ACL); 240 | UINT8_TO_STREAM (buf, ch & 0xFF); 241 | UINT8_TO_STREAM (buf, ((ch >> 8) & 0x0F) | pbf << 4 | bf << 6); 242 | UINT16_TO_STREAM (buf, l2capLen); 243 | 244 | return HCI_H4_ACL_PREAMBLE_SIZE + l2capLen; 245 | } 246 | 247 | TwHciInterface _hciInterface; 248 | 249 | static void sendHciPacket(uint8_t *data, size_t len) { 250 | VERBOSE_PRINTLN("sendHciPacket"); 251 | _hciInterface.hci_send_packet(data, len); 252 | } 253 | 254 | static int findItemsInArray(uint8_t* array, size_t arraySize, size_t itemLength, uint8_t* data, size_t dataLength, size_t alignment) { 255 | for(int i=0; i> 8); 546 | // Source CID: 0x0040+ 547 | payload[posi++] = (uint8_t)(cid & 0xFF); 548 | payload[posi++] = (uint8_t)(cid >> 8); 549 | uint16_t dataLen = posi; 550 | uint16_t len = make_acl_l2cap_packet(tmpQueueData, ch, pbf, bf, channelID, payload, dataLen); 551 | sendHciPacket(tmpQueueData, len); 552 | VERBOSE_PRINTLN("queued acl_l2cap_single_packet(CONNECTION REQUEST)"); 553 | } 554 | 555 | static void handleConnectionCompleteEvent(uint8_t len, uint8_t* data) { 556 | uint8_t status = data[0]; 557 | VERBOSE_PRINT("connection_complete status=%02X", status); 558 | 559 | uint16_t ch = data[2] << 8 | data[1]; // Connection Handle 560 | struct bd_addr_t bdAddr; 561 | STREAM_TO_BDADDR(bdAddr.addr, data+3); 562 | uint8_t lt = data[9]; // Link Type 563 | uint8_t ee = data[10]; // Encryption Enabled 564 | 565 | VERBOSE_PRINT(" Connection_Handle = 0x%04X", ch); 566 | VERBOSE_PRINT(" BD_ADDR = %s", format2Hex((uint8_t*)&bdAddr.addr, BD_ADDR_LEN)); 567 | VERBOSE_PRINT(" Link_Type = %02X", lt); 568 | VERBOSE_PRINT(" Encryption_Enabled = %02X", ee); 569 | 570 | l2capConnect(ch, 0x0013, 0x0045); 571 | } 572 | 573 | static void handleDisconnectionCompleteEvent(uint8_t len, uint8_t* data) { 574 | uint8_t status = data[0]; 575 | VERBOSE_PRINT("disconnection_complete status=%02X ", status); 576 | 577 | uint16_t ch = data[2] << 8 | data[1]; //Connection Handle 578 | uint8_t reason = data[3]; // Reason 579 | 580 | VERBOSE_PRINT("Connection_Handle = 0x%04X ", ch); 581 | VERBOSE_PRINT("Reason = %02X", reason); 582 | 583 | wiimoteConnected = false; 584 | resetDevice(); 585 | } 586 | 587 | void handleHciEvent(uint8_t event_code, uint8_t len, uint8_t* data) { 588 | VERBOSE_PRINTLN("handleHciEvent"); 589 | if(event_code != HCI_INQUIRY_RESULT_EVT){ // suppress HCI_INQUIRY_RESULT_EVT 590 | VERBOSE_PRINT("EVENT code=%02X len=%d data=%s", event_code, len, format2Hex(data, len)); 591 | } 592 | 593 | switch(event_code){ 594 | case HCI_INQUIRY_COMP_EVT: 595 | handleInquiryCompleteEvent(len, data);; 596 | break; 597 | case HCI_INQUIRY_RESULT_EVT: 598 | handleInquiryResultEvent(len, data);; 599 | break; 600 | case HCI_CONNECTION_COMP_EVT: 601 | handleConnectionCompleteEvent(len, data);; 602 | break; 603 | case HCI_DISCONNECTION_COMP_EVT: 604 | handleDisconnectionCompleteEvent(len, data);; 605 | break; 606 | case HCI_RMT_NAME_REQUEST_COMP_EVT: 607 | handleRemoteNameRequestCompleteEvent(len, data);; 608 | break; 609 | case HCI_COMMAND_COMPLETE_EVT: 610 | handleCommandCompleteEvent(len, data);; 611 | break; 612 | case HCI_COMMAND_STATUS_EVT: 613 | handleCommandStatusEvent(len, data);; 614 | break; 615 | default: 616 | // handleHciEvent no impl 617 | break; 618 | } 619 | 620 | } 621 | 622 | 623 | static void handleL2capConnectionResponse(uint16_t ch, uint8_t* data) { 624 | uint8_t identifier = data[1]; 625 | // uint16_t len = (data[3] << 8) | data[2]; 626 | uint16_t dstCID = (data[5] << 8) | data[4]; 627 | uint16_t srcCID = (data[7] << 8) | data[6]; 628 | uint16_t result = (data[9] << 8) | data[8]; 629 | uint16_t status = (data[11] << 8) | data[10]; 630 | 631 | // VERBOSE_PRINT("L2CAP CONNECTION RESPONSE"); 632 | VERBOSE_PRINT(" identifier = %02X", identifier); 633 | VERBOSE_PRINT(" dest cid = %04X", dstCID); 634 | VERBOSE_PRINT(" src cid = %04X", srcCID); 635 | VERBOSE_PRINT(" result = %04X", result); 636 | VERBOSE_PRINT(" status = %04X", status); 637 | 638 | if(result == 0x0000){ 639 | struct l2cap_connection_t connection; 640 | connection.ch = ch; 641 | connection.remoteCID = dstCID; 642 | int idx = l2capAddConnection(connection); 643 | if(idx == -1){ 644 | VERBOSE_PRINTLN("l2cap connection failed"); 645 | return; 646 | } 647 | uint8_t pbf = 0b10; // Packet Boundary Flag 648 | uint8_t bf = 0b00; // Broadcast Flag 649 | uint16_t channelID = 0x0001; 650 | 651 | // create command of 'Control frame' 652 | uint8_t posi = 0; 653 | // Command Header 654 | payload[posi++] = 0x04; // CODE:CONFIGURATION REQUEST 655 | payload[posi++] = 0x02; // Identifier 656 | payload[posi++] = 0x08; // Length: 0x0008 657 | payload[posi++] = 0x00; 658 | // Destination CID 659 | payload[posi++] = (uint8_t)(dstCID & 0xFF); 660 | payload[posi++] = (uint8_t)(dstCID >> 8); 661 | // Flags 662 | payload[posi++] = 0x00; 663 | payload[posi++] = 0x00; 664 | // type=01 len=02 value=00 40 665 | payload[posi++] = 0x01; 666 | payload[posi++] = 0x02; 667 | payload[posi++] = 0x40; 668 | payload[posi++] = 0x00; 669 | 670 | uint16_t dataLen = posi; 671 | uint16_t len = make_acl_l2cap_packet(tmpQueueData, ch, pbf, bf, channelID, payload, dataLen); 672 | sendHciPacket(tmpQueueData, len); 673 | VERBOSE_PRINTLN("queued acl_l2cap_single_packet(CONFIGURATION REQUEST)"); 674 | } 675 | } 676 | 677 | static void handleL2capConfigurationResponse(uint16_t ch, uint8_t* data) { 678 | uint8_t identifier = data[1]; 679 | uint16_t len = (data[3] << 8) | data[2]; 680 | uint16_t cid = (data[5] << 8) | data[4]; 681 | uint16_t flags = (data[7] << 8) | data[6]; 682 | uint16_t result = (data[9] << 8) | data[8]; 683 | // config = data[10..] 684 | 685 | // VERBOSE_PRINTLN("L2CAP CONFIGURATION RESPONSE "); 686 | VERBOSE_PRINT("identifier = %02X ", identifier); 687 | VERBOSE_PRINT("len = %04X ", len); 688 | VERBOSE_PRINT("cid = %04X ", cid); 689 | VERBOSE_PRINT("flags = %04X ", flags); 690 | VERBOSE_PRINT("result = %04X ", result); 691 | VERBOSE_PRINT("config = %s", format2Hex(data+10, len-6)); 692 | } 693 | 694 | static void handleL2capConfigurationRequest(uint16_t ch, uint8_t* data) { 695 | uint8_t identifier = data[1]; 696 | uint16_t len = (data[3] << 8) | data[2]; 697 | uint16_t dstCID = (data[5] << 8) | data[4]; 698 | uint16_t flags = (data[7] << 8) | data[6]; 699 | 700 | // VERBOSE_PRINTLN("L2CAP CONFIGURATION REQUEST"); 701 | VERBOSE_PRINT("identifier = %02X", identifier); 702 | VERBOSE_PRINT("len = %02X ", len); 703 | VERBOSE_PRINT("dstCID = %04X ", dstCID); 704 | VERBOSE_PRINT("flags = %04X ", flags); 705 | VERBOSE_PRINT("config = %s", format2Hex(data+8, len-4)); 706 | 707 | if(flags != 0x0000){ 708 | VERBOSE_PRINTLN("flags!=0x0000"); 709 | return; 710 | } 711 | if(len != 0x08){ 712 | VERBOSE_PRINTLN("len!=0x08"); 713 | return; 714 | } 715 | if((data[8] == 0x01) && (data[9] == 0x02)){ // MTU 716 | uint16_t mtu = (data[11] << 8) | data[10]; 717 | VERBOSE_PRINT(" MTU=%d", mtu); 718 | 719 | int idx = l2capFindConnection(ch); 720 | struct l2cap_connection_t connection = l2capConnectionList[idx]; 721 | 722 | uint8_t pbf = 0b10; // Packet Boundary Flag 723 | uint8_t bf = 0b00; // Broadcast Flag 724 | uint16_t channelID = 0x0001; 725 | uint16_t cid = connection.remoteCID; 726 | 727 | // create command of 'Control frame' 728 | uint8_t posi = 0; 729 | // Command Header 730 | payload[posi++] = 0x05; // CODE:CONFIGURATION RESPONSE 731 | payload[posi++] = identifier; // Identifier 732 | payload[posi++] = 0x0A; // Length: 0x000A 733 | payload[posi++] = 0x00; 734 | // Source CID 735 | payload[posi++] = (uint8_t)(cid & 0xFF); 736 | payload[posi++] = (uint8_t)(cid >> 8); 737 | // Flags 738 | payload[posi++] = 0x00; 739 | payload[posi++] = 0x00; 740 | // Res 741 | payload[posi++] = 0x00; 742 | payload[posi++] = 0x00; 743 | // type=01 len=02 value=xx xx 744 | payload[posi++] = 0x01; 745 | payload[posi++] = 0x02; 746 | payload[posi++] = (uint8_t)(mtu & 0xFF); 747 | payload[posi++] = (uint8_t)(mtu >> 8); 748 | 749 | uint16_t dataLen = posi; 750 | uint16_t len = make_acl_l2cap_packet(tmpQueueData, ch, pbf, bf, channelID, payload, dataLen); 751 | sendHciPacket(tmpQueueData, len); 752 | VERBOSE_PRINTLN("queued acl_l2cap_single_packet(CONFIGURATION RESPONSE)"); 753 | } 754 | } 755 | 756 | static void setPlayerLEDs(uint16_t ch, uint8_t leds) { 757 | int idx = l2capFindConnection(ch); 758 | struct l2cap_connection_t connection = l2capConnectionList[idx]; 759 | 760 | uint8_t pbf = 0b10; // Packet Boundary Flag 761 | uint8_t bf = 0b00; // Broadcast Flag 762 | uint16_t channelID = connection.remoteCID; 763 | 764 | // create information payload of 'Basic information frame' 765 | // wiimote report: (a2) 11 LL 766 | uint8_t posi = 0; 767 | // Information Payload 768 | payload[posi++] = 0xA2; // Output report 769 | payload[posi++] = 0x11; // Function:Player LEDs 770 | payload[posi++] = (uint8_t)(leds << 4); // LL:controls the four LEDs 771 | uint16_t dataLen = posi; 772 | uint16_t len = make_acl_l2cap_packet(tmpQueueData, ch, pbf, bf, channelID, payload, dataLen); 773 | sendHciPacket(tmpQueueData, len); 774 | VERBOSE_PRINT("queued acl_l2cap_single_packet(Set LEDs)"); 775 | } 776 | 777 | enum address_space_t { 778 | EEPROM_MEMORY, 779 | CONTROL_REGISTER 780 | }; 781 | 782 | static uint8_t getAddrSpace(int as) 783 | { 784 | switch(as){ 785 | case EEPROM_MEMORY : return 0x00; 786 | case CONTROL_REGISTER: return 0x04; 787 | } 788 | return 0xFF; 789 | } 790 | 791 | #define OFFSET_EEP_DATA (7) 792 | #define SIZE_EEP_DATA (16) 793 | 794 | static void writingEEPROM(uint16_t ch, int as, uint32_t offset, const uint8_t* eepData, uint8_t eepLen) { 795 | int idx = l2capFindConnection(ch); 796 | struct l2cap_connection_t connection = l2capConnectionList[idx]; 797 | 798 | uint8_t pbf = 0b10; // Packet Boundary Flag 799 | uint8_t bf = 0b00; // Broadcast Flag 800 | uint16_t channelID = connection.remoteCID; 801 | 802 | // create information payload of 'Basic information frame' 803 | // wiimote report: (a2) 16 MM FF FF FF SS DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD 804 | uint8_t posi = 0; 805 | // Information Payload 806 | payload[posi++] = 0xA2; // Output report 807 | payload[posi++] = 0x16; // Function:Write Memory and Registers 808 | payload[posi++] = getAddrSpace((int)as); // MM Address space: 0x00=EEPROM, 0x04=ControlRegister 809 | payload[posi++] = (uint8_t)((offset >> 16) & 0xFF); //FF 810 | payload[posi++] = (uint8_t)((offset >> 8) & 0xFF); //FF 811 | payload[posi++] = (uint8_t)((offset ) & 0xFF); //FF 812 | payload[posi++] = eepLen; // SS write eeprom size: 1..16 813 | memset(&payload[posi], 0, SIZE_EEP_DATA); // DD 814 | posi += SIZE_EEP_DATA; 815 | 816 | memcpy(payload+OFFSET_EEP_DATA, eepData, eepLen); 817 | 818 | uint16_t dataLen = posi; 819 | uint16_t len = make_acl_l2cap_packet(tmpQueueData, ch, pbf, bf, channelID, payload, dataLen); 820 | sendHciPacket(tmpQueueData, len); 821 | VERBOSE_PRINTLN("queued writingEEPROM"); 822 | } 823 | 824 | static void readingEEPROM(uint16_t ch, int as, uint32_t offset, uint16_t size) { 825 | int idx = l2capFindConnection(ch); 826 | struct l2cap_connection_t connection = l2capConnectionList[idx]; 827 | 828 | uint8_t pbf = 0b10; // Packet Boundary Flag 829 | uint8_t bf = 0b00; // Broadcast Flag 830 | uint16_t channelID = connection.remoteCID; 831 | 832 | // create information payload of 'Basic information frame' 833 | // wiimote report: (a2) 17 MM FF FF FF SS SS 834 | uint8_t posi = 0; 835 | // Information Payload 836 | payload[posi++] = 0xA2; // Output report 837 | payload[posi++] = 0x17; // Function:Read Memory and Registers 838 | payload[posi++] = getAddrSpace((int)as); // MM Address space: 0x00=EEPROM, 0x04=ControlRegister 839 | payload[posi++] = (uint8_t)((offset >> 16) & 0xFF); // FF 840 | payload[posi++] = (uint8_t)((offset >> 8) & 0xFF); // FF 841 | payload[posi++] = (uint8_t)((offset ) & 0xFF); // FF 842 | payload[posi++] = (uint8_t)((size >> 8 ) & 0xFF); // SS read eeprom size: 1..16 843 | payload[posi++] = (uint8_t)((size ) & 0xFF); // SS 844 | 845 | uint16_t dataLen = posi; 846 | uint16_t len = make_acl_l2cap_packet(tmpQueueData, ch, pbf, bf, channelID, payload, dataLen); 847 | sendHciPacket(tmpQueueData, len); 848 | VERBOSE_PRINTLN("queued readingEEPROM"); 849 | } 850 | 851 | static void setDataReportingMode(uint16_t ch, uint8_t mode, bool continuous) { 852 | int idx = l2capFindConnection(ch); 853 | struct l2cap_connection_t connection = l2capConnectionList[idx]; 854 | 855 | uint8_t pbf = 0b10; // Packet Boundary Flag 856 | uint8_t bf = 0b00; // Broadcast Flag 857 | uint16_t channelID = connection.remoteCID; 858 | uint8_t contReportIsDesired = continuous ? 0x04 : 0x00; // 0x00, 0x04 859 | 860 | // create information payload of 'Basic information frame' 861 | // report: (a2) 12 TT MM 862 | uint8_t posi = 0; 863 | // Information Payload 864 | payload[posi++] = 0xA2; // Output report 865 | payload[posi++] = 0x12; // Function:Data Reporting mode 866 | payload[posi++] = contReportIsDesired; // TT whether continuous reporting is desired 867 | payload[posi++] = mode; // MM 868 | 869 | uint16_t dataLen = posi; 870 | uint16_t len = make_acl_l2cap_packet(tmpQueueData, ch, pbf, bf, channelID, payload, dataLen); 871 | sendHciPacket(tmpQueueData, len); 872 | VERBOSE_PRINTLN("queued setDataReportingMode"); 873 | } 874 | 875 | enum { 876 | REPORT_STATE_INIT = 0, 877 | REPORT_STATE_WAIT_ACK_OUT_REPORT, 878 | REPORT_STATE_WAIT_READ_COTRLLER_TYPE, 879 | REPORT_STATE_WAIT_READ_RESPONSE, 880 | }; 881 | 882 | static void handleExtensionControllerReports(uint16_t ch, uint16_t channelID, uint8_t* data, uint16_t len) { 883 | static int controllerReportState = REPORT_STATE_INIT; 884 | 885 | switch(controllerReportState){ 886 | case REPORT_STATE_INIT: 887 | VERBOSE_PRINT("REPORT_STATE_INIT\n"); 888 | // data report(Status) 889 | // (a1) 20 BB BB LF 00 00 VV 890 | if(data[1] == 0x20){ 891 | if(data[4] & 0x02){ // extension controller is connected 892 | writingEEPROM(ch, CONTROL_REGISTER, 0xA400F0, (const uint8_t[]){0x55}, 1); 893 | controllerReportState = REPORT_STATE_WAIT_ACK_OUT_REPORT; 894 | }else{ // extension controller is NOT connected 895 | setDataReportingMode(ch, 0x30, false); // 0x30: Core Buttons : 30 BB BB 896 | // [note] Core Buttons and Accelerometer: 31 BB BB AA AA AA 897 | // [note] Core Buttons and Accelerometer with 12 IR bytes: 33 BB BB AA AA AA II II II II II II II II II II II II 898 | } 899 | } 900 | break; 901 | case REPORT_STATE_WAIT_ACK_OUT_REPORT: 902 | VERBOSE_PRINT("REPORT_STATE_WAIT_ACK_OUT_REPORT\n"); 903 | // data report(Acknowledge output report, return function result) 904 | // (a1) 22 BB BB 16 00 : OK 905 | // (a1) 22 BB BB 16 04 : NG 906 | if((data[1] == 0x22) && (data[4] == 0x16)){ 907 | if(data[5] == 0x00){ 908 | writingEEPROM(ch, CONTROL_REGISTER, 0xA400FB, (const uint8_t[]){0x00}, 1); 909 | controllerReportState = REPORT_STATE_WAIT_READ_COTRLLER_TYPE; 910 | }else{ 911 | controllerReportState = REPORT_STATE_INIT; 912 | } 913 | } 914 | break; 915 | case REPORT_STATE_WAIT_READ_COTRLLER_TYPE: 916 | VERBOSE_PRINT("REPORT_STATE_WAIT_READ_COTRLLER_TYPE\n"); 917 | if((data[1] == 0x22) && (data[4] == 0x16)){ 918 | if(data[5] == 0x00){ 919 | readingEEPROM(ch, CONTROL_REGISTER, 0xA400FA, 6); // read controller type 920 | controllerReportState = REPORT_STATE_WAIT_READ_RESPONSE; 921 | }else{ 922 | controllerReportState = REPORT_STATE_INIT; 923 | } 924 | } 925 | break; 926 | case REPORT_STATE_WAIT_READ_RESPONSE: 927 | VERBOSE_PRINT("REPORT_STATE_WAIT_READ_RESPONSE\n"); 928 | // data report(Read response) 929 | // (a1) 21 BB BB SE FF FF DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD 930 | if(data[1] == 0x21){ 931 | if(memcmp(data+5, (const uint8_t[]){0x00, 0xFA}, 2) == 0){ 932 | if(memcmp(data+7, (const uint8_t[]){0x00, 0x00, 0xA4, 0x20, 0x00, 0x00}, 6) == 0){ // Nunchuck 933 | setDataReportingMode(ch, 0x32, false); // 0x32: Core Buttons with 8 Extension bytes : 32 BB BB EE EE EE EE EE EE EE EE 934 | } 935 | controllerReportState = REPORT_STATE_INIT; 936 | } 937 | } 938 | break; 939 | } 940 | } 941 | 942 | 943 | /** 944 | * Received Data 945 | */ 946 | struct recv_data_rb { 947 | uint8_t wp; 948 | uint8_t rp; 949 | uint8_t cnt; 950 | }; 951 | recv_data_rb receivedDataRb; 952 | #define RECIEVED_DATA_MAX_NUM (5) 953 | TinyWiimoteData receivedData[RECIEVED_DATA_MAX_NUM]; 954 | 955 | void putWiimoteReceivedData(uint8_t number, uint8_t* data, uint8_t len) { 956 | if(receivedDataRb.cnt < RECIEVED_DATA_MAX_NUM) { 957 | TinyWiimoteData *target = &(receivedData[receivedDataRb.wp]); 958 | memcpy(target->data, data, len); 959 | target->number = number; 960 | target->len = len; 961 | receivedDataRb.wp = (receivedDataRb.wp + 1) % RECIEVED_DATA_MAX_NUM; 962 | receivedDataRb.cnt++; 963 | } 964 | VERBOSE_PRINTLN(""); 965 | } 966 | 967 | static void handleReport(uint8_t* data, uint16_t len) { 968 | VERBOSE_PRINT("REPORT len=%d data=%s", len, format2Hex(data, len)); 969 | uint8_t idx = 0; //only supports one wiimote 970 | putWiimoteReceivedData(idx, data, len); 971 | } 972 | 973 | static void handleL2capData(uint16_t ch, uint16_t channelID, uint8_t* data, uint16_t len) { 974 | VERBOSE_PRINTLN("handleL2capData"); 975 | VERBOSE_PRINT("data[0]=%02X\n", data[0]); 976 | 977 | switch(data[0]) { 978 | case L2CAP_CONNECT_RES: 979 | VERBOSE_PRINT("L2CAP CONNECTION RESPONSE"); 980 | handleL2capConnectionResponse(ch, data); 981 | break; 982 | case L2CAP_CONFIG_REQ: 983 | VERBOSE_PRINTLN("L2CAP CONFIGURATION REQUEST"); 984 | handleL2capConfigurationRequest(ch, data); 985 | break; 986 | case L2CAP_CONFIG_RES: 987 | VERBOSE_PRINTLN("L2CAP CONFIGURATION RESPONSE"); 988 | handleL2capConfigurationResponse(ch, data); 989 | break; 990 | case BTCODE_HID: 991 | if(!wiimoteConnected){ 992 | setPlayerLEDs(ch, 0b0001); 993 | wiimoteConnected = true; 994 | } 995 | handleExtensionControllerReports(ch, channelID, data, len); 996 | handleReport(data, len); 997 | break; 998 | default: 999 | // handleL2capData no impl 1000 | VERBOSE_PRINT(" L2CAP len=%d data=%s\n", len, format2Hex(data, len)); 1001 | break; 1002 | } 1003 | 1004 | } 1005 | 1006 | void handleAclData(uint8_t* data, size_t len) { 1007 | VERBOSE_PRINT("handleAclData\n"); 1008 | if(!wiimoteConnected){ 1009 | VERBOSE_PRINT("len=%d data=%s\n", len, format2Hex(data, len)); 1010 | } 1011 | 1012 | uint16_t ch = ((data[1] & 0x0F) << 8) | data[0]; // Connection Handle 1013 | uint8_t pbf = (data[1] & 0x30) >> 4; // Packet Boundary Flag 1014 | uint8_t bf = (data[1] & 0xC0) >> 6; // Broadcast Flag 1015 | uint16_t aclLen = (data[3] << 8) | data[2]; 1016 | if(pbf != 0b10){ 1017 | VERBOSE_PRINT("packet boundary flag = %d, aclLen = %d", pbf, aclLen); 1018 | return; 1019 | } 1020 | if(bf != 0b00){ 1021 | VERBOSE_PRINT("bf = %d", bf); 1022 | return; 1023 | } 1024 | uint16_t l2capLen = (data[5] << 8) | data[4]; 1025 | uint16_t channelID = (data[7] << 8) | data[6]; 1026 | 1027 | handleL2capData(ch, channelID, data + 8, l2capLen); 1028 | } 1029 | 1030 | void handleHciData(uint8_t* data, size_t len) { 1031 | switch(data[0]){ 1032 | case H4_TYPE_EVENT: 1033 | handleHciEvent(data[1], data[2], data+3); 1034 | break; 1035 | case H4_TYPE_ACL: 1036 | handleAclData(data+1, len-1); 1037 | break; 1038 | default: 1039 | VERBOSE_PRINT("UNKNOWN EVENT"); 1040 | VERBOSE_PRINT("len=%d data=%s", len, format2Hex(data, len)); 1041 | } 1042 | } 1043 | 1044 | bool TinyWiimoteDeviceIsInited(void) { 1045 | return deviceInited; 1046 | } 1047 | 1048 | int TinyWiimoteAvailable() { 1049 | return receivedDataRb.cnt; 1050 | } 1051 | 1052 | TinyWiimoteData TinyWiimoteRead() { 1053 | TinyWiimoteData target; 1054 | target.number = 0; 1055 | target.len = 0; 1056 | if(receivedDataRb.cnt > 0) { 1057 | target = receivedData[receivedDataRb.rp]; 1058 | receivedDataRb.rp = (receivedDataRb.rp + 1) % RECIEVED_DATA_MAX_NUM; 1059 | receivedDataRb.cnt--; 1060 | } 1061 | return target; 1062 | } 1063 | 1064 | void TinyWiimoteInit(TwHciInterface hciInterface) { 1065 | receivedDataRb.cnt = 0; 1066 | receivedDataRb.wp = 0; 1067 | receivedDataRb.rp = 0; 1068 | _hciInterface = hciInterface; 1069 | } 1070 | -------------------------------------------------------------------------------- /TinyWiimote.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Daiki Yasuda 2 | // 3 | // This is licensed under 4 | // - Creative Commons Attribution-NonCommercial 3.0 Unported 5 | // - https://creativecommons.org/licenses/by-nc/3.0/ 6 | // - Or see LICENSE.md 7 | // 8 | // The short of it is... 9 | // You are free to: 10 | // Share — copy and redistribute the material in any medium or format 11 | // Adapt — remix, transform, and build upon the material 12 | // Under the following terms: 13 | // NonCommercial — You may not use the material for commercial purposes. 14 | 15 | #ifndef _TINY_WIIMOTE_H_ 16 | #define _TINY_WIIMOTE_H_ 17 | 18 | #define RECIEVED_DATA_MAX_LEN (50) 19 | struct TinyWiimoteData { 20 | uint8_t number; 21 | uint8_t data[RECIEVED_DATA_MAX_LEN]; 22 | uint8_t len; 23 | }; 24 | #define TWII_OFFSET_BTNS1 (2) 25 | #define TWII_OFFSET_BTNS2 (3) 26 | #define TWII_OFFSET_EXTCTRL (4) // Offset for Extension Controllers data 27 | 28 | typedef struct tinywii_device_callback { 29 | void (*hci_send_packet)(uint8_t *data, size_t len); 30 | } TwHciInterface; 31 | 32 | void TinyWiimoteInit(TwHciInterface hciInterface); 33 | int TinyWiimoteAvailable(void); 34 | TinyWiimoteData TinyWiimoteRead(void); 35 | 36 | void TinyWiimoteResetDevice(void); 37 | bool TinyWiimoteDeviceIsInited(void); 38 | void handleHciData(uint8_t* data, size_t len); 39 | 40 | char* format2Hex(uint8_t* data, uint16_t len); 41 | 42 | #endif // _TINY_WIIMOTE_H_ 43 | -------------------------------------------------------------------------------- /examples/ESP32WiimoteDemo/ESP32WiimoteDemo.ino: -------------------------------------------------------------------------------- 1 | #include "ESP32Wiimote.h" 2 | 3 | ESP32Wiimote wiimote; 4 | 5 | void setup() 6 | { 7 | Serial.begin(115200); 8 | wiimote.init(); 9 | } 10 | 11 | void loop() 12 | { 13 | wiimote.task(); 14 | if (wiimote.available() > 0) { 15 | uint16_t button = wiimote.getButtonState(); 16 | Serial.printf("%04x\n", button); 17 | if (button == ESP32Wiimote::BUTTON_A) { 18 | Serial.println("A button"); 19 | } 20 | } 21 | delay(10); 22 | } 23 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP32Wiimote 2 | version=0.1.3 3 | author=Daiki 4 | maintainer=Daiki 5 | sentence=Wiimote Library for ESP32 6 | paragraph=library that connects with a Wii remote. Supported chip ESP32. Included with some examples for real application. 7 | category=Communication 8 | url=https://github.com/bigw00d/ESP32Wiimote 9 | architectures=* -------------------------------------------------------------------------------- /remocon_led1_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigw00d/Arduino-ESP32Wiimote/add81fc92eb49c42c64753a06b1f3178c89adc7b/remocon_led1_on.png --------------------------------------------------------------------------------