├── .editorconfig ├── LICENSE ├── README.md ├── controller └── controller.ino ├── ds2-emulator.ino ├── dumps ├── pop_ds2.dsl └── pop_emu.dsl └── uhs-ps4bt └── uhs-ps4bt.ino /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | end_of_line = lf 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Giacomo Gregoletto 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DualShock 2 emulator for Arduino Uno 2 | 3 | Emulate a DualShock 2 controller with Arduino Uno and control It throught I2C bus. 4 | 5 | ## Hardware 6 | 7 | To be able to communicate with the PlayStation 2, the circuit connected 8 | to **pin 13** needs to be removed somehow. 9 | The circuit consists in a led and a resistor: 10 | you can desolder one component, both, or just cut the connection between them. 11 | 12 | ## Software 13 | 14 | Install [this](https://github.com/NicksonYap/digitalWriteFast) library. 15 | 16 | ## Wiring 17 | 18 | | Function | Arduino | Wire | 19 | | -------- | ------- | ------ | 20 | | GND | GND | Black | 21 | | ACK | 9 | Green | 22 | | SS | 10 | Yellow | 23 | | MOSI | 11 | Orange | 24 | | MISO | 12 | Brown | 25 | | SCK | 13 | Blue | 26 | | SDA | A4 | - | 27 | | SCL | A5 | - | 28 | 29 | ## I2C 30 | 31 | Default address: **8** 32 | 33 | | Byte | Target | Details | 34 | | ---- | ------------------ | --------------------- | 35 | | 0 | Buttons | see below | 36 | | 1 | Buttons | see below | 37 | | 2 | Right stick X-axis | 0x00=Left, 0xFF=Right | 38 | | 3 | Right stick Y-axis | 0x00=Up, 0xFF=Down | 39 | | 4 | Left stick X-axis | 0x00=Left, 0xFF=Right | 40 | | 5 | Left stick Y-axis | 0x00=Up, 0xFF=Down | 41 | 42 | ### Byte 0 43 | 44 | **LSB, active low.** 45 | 46 | - Select 47 | - L3 48 | - R3 49 | - Start 50 | - Up 51 | - Right 52 | - Down 53 | - Left 54 | 55 | ### Byte 1 56 | 57 | **LSB, active low.** 58 | 59 | - L2 60 | - R2 61 | - L1 62 | - R1 63 | - Triangle 64 | - Circle 65 | - Cross 66 | - Square 67 | 68 | ## Resources 69 | 70 | - http://avrbeginners.net/architecture/spi/spi.html 71 | - https://gist.github.com/scanlime/5042071 72 | - http://store.curiousinventor.com/guides/PS2/ 73 | - http://www.lynxmotion.com/images/files/ps2cmd01.txt 74 | - http://blog.nearfuturelaboratory.com/category/psx/ 75 | - http://procrastineering.blogspot.com/2010/12/simulated-ps2-controller-for.html 76 | -------------------------------------------------------------------------------- /controller/controller.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define I2C_ADDRESS 8 5 | 6 | #define PIN_CROSS 4 7 | #define PIN_CIRCLE 5 8 | #define PIN_TRIANGLE 6 9 | #define PIN_SQUARE 7 10 | 11 | #define PIN_UP 8 12 | #define PIN_DOWN 9 13 | #define PIN_LEFT 10 14 | #define PIN_RIGH 11 15 | 16 | void _write () { 17 | byte tx[2] = { 0xFF, 0xFF }; 18 | 19 | if (!digitalReadFast(PIN_CROSS)) bitClear(tx[1], 6); 20 | if (!digitalReadFast(PIN_CIRCLE)) bitClear(tx[1], 5); 21 | if (!digitalReadFast(PIN_TRIANGLE)) bitClear(tx[1], 4); 22 | if (!digitalReadFast(PIN_SQUARE)) bitClear(tx[1], 7); 23 | if (!digitalReadFast(PIN_UP)) bitClear(tx[0], 4); 24 | if (!digitalReadFast(PIN_DOWN)) bitClear(tx[0], 6); 25 | if (!digitalReadFast(PIN_LEFT)) bitClear(tx[0], 7); 26 | if (!digitalReadFast(PIN_RIGH)) bitClear(tx[0], 5); 27 | 28 | Wire.beginTransmission(I2C_ADDRESS); 29 | Wire.write(tx[0]); 30 | Wire.write(tx[1]); 31 | Wire.write(0x7F); 32 | Wire.write(0x7F); 33 | Wire.write(0x7F); 34 | Wire.write(0x7F); 35 | Wire.endTransmission(); 36 | } 37 | 38 | void setup () { 39 | pinMode(PIN_CROSS, INPUT_PULLUP); 40 | pinMode(PIN_CIRCLE, INPUT_PULLUP); 41 | pinMode(PIN_TRIANGLE, INPUT_PULLUP); 42 | pinMode(PIN_SQUARE, INPUT_PULLUP); 43 | pinMode(PIN_UP, INPUT_PULLUP); 44 | pinMode(PIN_DOWN, INPUT_PULLUP); 45 | pinMode(PIN_LEFT, INPUT_PULLUP); 46 | pinMode(PIN_RIGH, INPUT_PULLUP); 47 | 48 | Wire.begin(); 49 | } 50 | 51 | void loop () { 52 | _write(); 53 | delay(25); 54 | } 55 | -------------------------------------------------------------------------------- /ds2-emulator.ino: -------------------------------------------------------------------------------- 1 | #include // https://github.com/NicksonYap/digitalWriteFast 2 | #include 3 | #include 4 | 5 | /** 6 | * Arduino Uno pinout (and remove the resistor on pin 13) 7 | */ 8 | #define ACK 9 // green 9 | // SS 10 // yellow 10 | // MOSI 11 // orange 11 | // MISO 12 // brown 12 | // SCK 13 // blue 13 | 14 | /** 15 | * I2C slave address 16 | */ 17 | #define I2C_ADDRESS 8 18 | 19 | // Config mode (has precedence) 20 | bool CONFIG = false; 21 | 22 | // 23 | volatile bool NEXT_CONFIG = false; 24 | 25 | // Analog mode (pressure levels supported only on analog mode) 26 | volatile bool ANALOG = false; 27 | 28 | // How many bytes have payload on analog mode 29 | volatile uint8_t ANALOG_LENGTH = 6; 30 | 31 | // 32 | byte ANALOG_MAPPING[3] = { 0x3F, 0x00, 0x00 }; 33 | 34 | // When true you cannot change the current mode by pressing the controller button 35 | volatile bool LOCKED = false; 36 | 37 | // Previous (negated) status of the SS pin 38 | bool ATTENTION = false; 39 | 40 | // Expected communication length in bytes 41 | uint8_t LENGTH = 0; 42 | 43 | // Current communication byte index 44 | uint8_t INDEX = 0; 45 | 46 | // Current command from PlayStation (header, second byte) 47 | volatile byte CMD = 0x5A; 48 | 49 | // Small motor 50 | bool SMOTOR_STATUS = false; 51 | uint8_t SMOTOR_INDEX = 69; 52 | 53 | // Large motor 54 | byte LMOTOR_STATUS = 0x00; 55 | uint8_t LMOTOR_INDEX = 69; 56 | 57 | // 58 | bool SEND_ACK = false; 59 | 60 | // Payload used during the config mode (calculated) 61 | byte DATA_CFG[6] = { 62 | 0x00, 63 | 0x00, 64 | 0x00, 65 | 0x00, 66 | 0x00, 67 | 0x00 68 | }; 69 | 70 | // Payload used during the other modes (represents buttons status) 71 | byte DATA_BTN[18] = { 72 | // Left, Down, Right, Up, Start, R3, L3, Select (0 is pressed) 73 | // 0xFE > Select is pressed 74 | 0xFF, 75 | 76 | // Square, Cross, Circle, Triangle, R1, L1 ,R2, L2 (0 is pressed) 77 | // 0xFE > L2 is pressed 78 | 0xFF, 79 | 80 | // Analog sticks 81 | 0x7F, // Right X-axis, 0x00 = Left, 0xFF = Right 82 | 0x7F, // Right Y-axis, 0x00 = Up, 0xFF = Down 83 | 0x7F, // Left X-axis, 0x00 = Left, 0xFF = Right 84 | 0x7F, // Left Y-axis, 0x00 = Up, 0xFF = Down 85 | 86 | // Pressure levels (0xFF is fully pressed) 87 | 0x00, // Right 88 | 0x00, // Left 89 | 0x00, // Up 90 | 0x00, // Down 91 | 0x00, // Triangle 92 | 0x00, // Circle 93 | 0x00, // Cross 94 | 0x00, // Square 95 | 0x00, // L1 96 | 0x00, // R1 97 | 0x00, // L2 98 | 0x00 // R2 99 | }; 100 | 101 | /** 102 | * Sync pressure levels by reading from digital status 103 | */ 104 | void syncPressureLevels () { 105 | DATA_BTN[ 7] = bitRead(DATA_BTN[0], 7) ? 0x00 : 0xFF; // Left 106 | DATA_BTN[ 9] = bitRead(DATA_BTN[0], 6) ? 0x00 : 0xFF; // Down 107 | DATA_BTN[ 6] = bitRead(DATA_BTN[0], 5) ? 0x00 : 0xFF; // Right 108 | DATA_BTN[ 8] = bitRead(DATA_BTN[0], 4) ? 0x00 : 0xFF; // Up 109 | DATA_BTN[13] = bitRead(DATA_BTN[1], 7) ? 0x00 : 0xFF; // Square 110 | DATA_BTN[12] = bitRead(DATA_BTN[1], 6) ? 0x00 : 0xFF; // Cross 111 | DATA_BTN[11] = bitRead(DATA_BTN[1], 5) ? 0x00 : 0xFF; // Circle 112 | DATA_BTN[10] = bitRead(DATA_BTN[1], 4) ? 0x00 : 0xFF; // Triangle 113 | DATA_BTN[15] = bitRead(DATA_BTN[1], 3) ? 0x00 : 0xFF; // R1 114 | DATA_BTN[14] = bitRead(DATA_BTN[1], 2) ? 0x00 : 0xFF; // L1 115 | DATA_BTN[17] = bitRead(DATA_BTN[1], 1) ? 0x00 : 0xFF; // R2 116 | DATA_BTN[16] = bitRead(DATA_BTN[1], 0) ? 0x00 : 0xFF; // L2 117 | } 118 | 119 | /** 120 | * Count how many bits are set inside a byte 121 | */ 122 | uint8_t countBits (byte value) { 123 | uint8_t count = 0; 124 | for (uint8_t i = 0; i < 8; i++) { 125 | count += (value >> i) & 0x01; 126 | } 127 | return count; 128 | } 129 | 130 | /** 131 | * Set ANALOG_LENGTH by calculating how many bits are set inside the mapping bytes (and check the max length) 132 | */ 133 | void updateAnalogLength () { 134 | ANALOG_LENGTH = countBits(ANALOG_MAPPING[0]) + countBits(ANALOG_MAPPING[1]) + countBits(ANALOG_MAPPING[2]); 135 | if (ANALOG_LENGTH > 18) { 136 | ANALOG_LENGTH = 18; 137 | } 138 | } 139 | 140 | /** 141 | * Read current mode code 142 | */ 143 | byte getControllerMode () { 144 | if (CONFIG) { 145 | return 0xF3; 146 | } else if (!ANALOG) { 147 | return 0x41; 148 | } else { 149 | return 0x70 | (ANALOG_LENGTH / 2); 150 | } 151 | } 152 | 153 | /** 154 | * Init response payload for the config mode 155 | */ 156 | void initConfigResponse () { 157 | DATA_CFG[0] = 0x00; 158 | DATA_CFG[1] = 0x00; 159 | DATA_CFG[2] = 0x00; 160 | DATA_CFG[3] = 0x00; 161 | DATA_CFG[4] = 0x00; 162 | DATA_CFG[5] = 0x00; 163 | 164 | switch (CMD) { 165 | case 0x41: 166 | if (ANALOG) { 167 | DATA_CFG[0] = ANALOG_MAPPING[0]; 168 | DATA_CFG[1] = ANALOG_MAPPING[1]; 169 | DATA_CFG[2] = ANALOG_MAPPING[2]; 170 | DATA_CFG[5] = 0x5A; 171 | } 172 | break; 173 | 174 | case 0x42: 175 | NEXT_CONFIG = false; 176 | break; 177 | 178 | case 0x44: 179 | ANALOG_LENGTH = 6; 180 | ANALOG_MAPPING[0] = 0x3F; 181 | ANALOG_MAPPING[1] = 0x00; 182 | ANALOG_MAPPING[2] = 0x00; 183 | break; 184 | 185 | case 0x45: 186 | DATA_CFG[0] = 0x03; // DualShock 2 controller constant 187 | DATA_CFG[1] = 0x02; 188 | DATA_CFG[2] = ANALOG ? 0x01 : 0x00; 189 | DATA_CFG[3] = 0x02; 190 | DATA_CFG[4] = 0x01; 191 | DATA_CFG[5] = 0x00; 192 | break; 193 | 194 | case 0x47: 195 | DATA_CFG[2] = 0x02; 196 | DATA_CFG[4] = 0x01; 197 | break; 198 | 199 | case 0x4D: 200 | // Turn off and disable the small motor 201 | SMOTOR_INDEX = 69; 202 | SMOTOR_STATUS = false; 203 | // Turn off and disable the large motor 204 | LMOTOR_INDEX = 69; 205 | LMOTOR_STATUS = 0x00; 206 | // Setup response 207 | DATA_CFG[0] = 0xFF; 208 | DATA_CFG[1] = 0xFF; 209 | DATA_CFG[2] = 0xFF; 210 | DATA_CFG[3] = 0xFF; 211 | DATA_CFG[4] = 0xFF; 212 | DATA_CFG[5] = 0xFF; 213 | break; 214 | 215 | case 0x4F: 216 | DATA_CFG[5] = 0x5A; 217 | break; 218 | } 219 | } 220 | 221 | /** 222 | * Process response from PlayStation 223 | */ 224 | void processConfigResponse (byte rx) { 225 | switch (CMD) { 226 | case 0x43: 227 | if (INDEX == 3 && rx == 0x00) { 228 | NEXT_CONFIG = false; 229 | } 230 | break; 231 | 232 | case 0x44: 233 | if (INDEX == 3 && rx == 0x01) { 234 | ANALOG = true; 235 | } else if (INDEX == 3 && rx == 0x00) { 236 | ANALOG = false; 237 | } else if (INDEX == 4 && rx == 0x03) { 238 | LOCKED = true; 239 | } 240 | break; 241 | 242 | case 0x46: 243 | if (INDEX == 3 && rx == 0x00) { 244 | DATA_CFG[2] = 0x01; 245 | DATA_CFG[3] = 0x02; 246 | DATA_CFG[5] = 0x0A; 247 | } else if (INDEX == 3 && rx == 0x01) { 248 | DATA_CFG[2] = 0x01; 249 | DATA_CFG[3] = 0x01; 250 | DATA_CFG[4] = 0x01; 251 | DATA_CFG[5] = 0x14; 252 | } 253 | break; 254 | 255 | case 0x4C: 256 | if (INDEX == 3 && rx == 0x00) { 257 | DATA_CFG[3] = 0x04; 258 | } else if (INDEX == 3 && rx == 0x01) { 259 | DATA_CFG[3] = 0x07; 260 | } 261 | break; 262 | 263 | case 0x4D: 264 | if (rx == 0x00) { 265 | SMOTOR_INDEX = INDEX; 266 | } else if (rx == 0x01) { 267 | LMOTOR_INDEX = INDEX; 268 | } 269 | break; 270 | 271 | case 0x4F: 272 | if (INDEX >= 3 && INDEX <= 5) { 273 | ANALOG_MAPPING[INDEX - 3] = rx; 274 | ANALOG_LENGTH = countBits(ANALOG_MAPPING[0]) + countBits(ANALOG_MAPPING[1]) + countBits(ANALOG_MAPPING[2]); 275 | } 276 | break; 277 | } 278 | } 279 | 280 | /** 281 | * Reset current communication status (prepare for the next one) 282 | */ 283 | void resetStatus () { 284 | // First SPI byte sent 285 | SPDR = 0xFF; 286 | 287 | // Reset byte index 288 | INDEX = 0; 289 | 290 | // Reset command 291 | CMD = 0x5A; 292 | 293 | // Set required config mode 294 | CONFIG = NEXT_CONFIG; 295 | 296 | // Calculate message length 297 | if (CONFIG) { 298 | LENGTH = 9; 299 | } else if (!ANALOG) { 300 | LENGTH = 5; 301 | } else { 302 | LENGTH = ANALOG_LENGTH + 3; 303 | } 304 | } 305 | 306 | /** 307 | * TODO 308 | */ 309 | byte getResponseByte () { 310 | if (INDEX >= LENGTH) { 311 | return 0xFF; 312 | } else if (INDEX == 0) { 313 | return getControllerMode(); 314 | } else if (INDEX == 1) { 315 | return 0x5A; 316 | } else if (CONFIG) { 317 | return DATA_CFG[INDEX - 2]; 318 | } else { 319 | return DATA_BTN[INDEX - 2]; 320 | } 321 | } 322 | 323 | /** 324 | * SPI interrupt routine 325 | */ 326 | ISR (SPI_STC_vect) { 327 | // Process received byte 328 | byte rx = SPDR; 329 | if (INDEX == 1) { 330 | CMD = rx; 331 | if (CONFIG) { 332 | initConfigResponse(); 333 | } 334 | } 335 | if (CONFIG) { 336 | processConfigResponse(rx); 337 | } else if (CMD == 0x42 && SMOTOR_INDEX == INDEX) { 338 | SMOTOR_STATUS = rx == 0xFF; 339 | } else if (CMD == 0x42 && LMOTOR_INDEX == INDEX) { 340 | LMOTOR_STATUS = rx; 341 | } else if (CMD == 0x43 && INDEX == 3 && rx == 0x01) { 342 | NEXT_CONFIG = true; 343 | } 344 | 345 | // Set next response byte 346 | SPDR = getResponseByte(); 347 | 348 | // Ready for the next incoming byte 349 | INDEX++; 350 | 351 | // Schedule ACK signal 352 | SEND_ACK = true; 353 | } 354 | 355 | /** 356 | * I2C incoming data interrupt 357 | */ 358 | void receiveEvent (int count) { 359 | byte data; 360 | for (uint8_t i = 0; i < count; i++) { 361 | data = Wire.read(); 362 | if (i < 6) { 363 | DATA_BTN[i] = data; 364 | } 365 | } 366 | syncPressureLevels(); 367 | } 368 | 369 | /** 370 | * I2C outcoming data interrupt 371 | */ 372 | void requestEvent () { 373 | byte data = 0x00; 374 | bitWrite(data, 0, ANALOG); 375 | bitWrite(data, 1, SMOTOR_STATUS); 376 | bitWrite(data, 2, LOCKED); 377 | Wire.write(data); 378 | Wire.write(LMOTOR_STATUS); 379 | } 380 | 381 | /** 382 | * setup 383 | */ 384 | void setup () { 385 | // Inputs 386 | pinMode(MOSI, INPUT); 387 | pinMode(SCK, INPUT); 388 | pinMode(SS, INPUT); 389 | 390 | // Outputs 391 | pinMode(MISO, OUTPUT); 392 | pinMode(ACK, OUTPUT); 393 | 394 | // I2C setup 395 | Wire.begin(I2C_ADDRESS); 396 | Wire.onReceive(receiveEvent); 397 | Wire.onRequest(requestEvent); 398 | 399 | // Configure SPI mode (DORD=1, CPOL=1, CPHA=1) 400 | SPCR |= bit (SPE); 401 | SPCR |= 0x2C; 402 | 403 | // Init outputs 404 | digitalWrite(ACK, HIGH); 405 | 406 | // Enable SPI interrupt 407 | SPCR |= _BV(SPIE); 408 | 409 | // Init controller status 410 | resetStatus(); 411 | } 412 | 413 | /** 414 | * loop 415 | */ 416 | void loop () { 417 | // Reset status when there's no attention 418 | bool attention = !digitalReadFast(SS); 419 | if (!attention && ATTENTION) { 420 | resetStatus(); 421 | } 422 | ATTENTION = attention; 423 | 424 | // Send ACK signal when required 425 | if (SEND_ACK) { 426 | SEND_ACK = false; 427 | digitalWriteFast(ACK, LOW); 428 | delayMicroseconds(4); 429 | digitalWriteFast(ACK, HIGH); 430 | } 431 | } 432 | -------------------------------------------------------------------------------- /dumps/pop_ds2.dsl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greguz/ds2-emulator/de010d9990d2c410edb14af2e01d66a26c18c4e4/dumps/pop_ds2.dsl -------------------------------------------------------------------------------- /dumps/pop_emu.dsl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greguz/ds2-emulator/de010d9990d2c410edb14af2e01d66a26c18c4e4/dumps/pop_emu.dsl -------------------------------------------------------------------------------- /uhs-ps4bt/uhs-ps4bt.ino: -------------------------------------------------------------------------------- 1 | // WARNING: SET OPTIMIZE TO "FAST" 2 | // https://github.com/felis/USB_Host_Shield_2.0/issues/402 3 | 4 | #define I2C_ADDRESS 8 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef dobogusinclude 11 | #include 12 | #endif 13 | #include 14 | 15 | USB Usb; 16 | 17 | // Some dongles have a hub inside 18 | // USBHub Hub1(&Usb); 19 | 20 | // You have to create the Bluetooth Dongle instance like so 21 | BTD Btd(&Usb); 22 | 23 | // This will start an inquiry and then pair with the PS4 controller - you only have to do this once 24 | // You will need to hold down the PS and Share button at the same time, the PS4 controller will then start to blink rapidly indicating that it is in pairing mode 25 | PS4BT PS4(&Btd, PAIR); 26 | 27 | // After that you can simply create the instance like so and then press the PS button on the device 28 | // PS4BT PS4(&Btd); 29 | 30 | // TODO: Rumble 31 | // PS4.setRumbleOn(RumbleLow); 32 | // PS4.setRumbleOn(RumbleHigh); 33 | 34 | // TODO: Digital mode 35 | // PS4.setLed(Blue); 36 | 37 | // TODO: Analog mode 38 | // PS4.setLed(Red); 39 | 40 | unsigned long lastSync = 0; 41 | 42 | byte data[6] = { 43 | 0xFF, 44 | 0xFF, 45 | 0x7F, 46 | 0x7F, 47 | 0x7F, 48 | 0x7F 49 | }; 50 | 51 | void setup () { 52 | Wire.begin(); 53 | 54 | if (Usb.Init() == -1) { 55 | while (1); // Halt 56 | } 57 | } 58 | 59 | void loop () { 60 | Usb.Task(); 61 | 62 | if (PS4.connected()) { 63 | data[0] = 0xFF; 64 | if (PS4.getButtonPress(SHARE)) { 65 | bitClear(data[0], 0); 66 | } 67 | if (PS4.getButtonPress(L3)) { 68 | bitClear(data[0], 1); 69 | } 70 | if (PS4.getButtonPress(R3)) { 71 | bitClear(data[0], 2); 72 | } 73 | if (PS4.getButtonPress(OPTIONS)) { 74 | bitClear(data[0], 3); 75 | } 76 | if (PS4.getButtonPress(UP)) { 77 | bitClear(data[0], 4); 78 | } 79 | if (PS4.getButtonPress(RIGHT)) { 80 | bitClear(data[0], 5); 81 | } 82 | if (PS4.getButtonPress(DOWN)) { 83 | bitClear(data[0], 6); 84 | } 85 | if (PS4.getButtonPress(LEFT)) { 86 | bitClear(data[0], 7); 87 | } 88 | 89 | data[1] = 0xFF; 90 | if (PS4.getAnalogButton(L2)) { 91 | bitClear(data[1], 0); 92 | } 93 | if (PS4.getAnalogButton(R2)) { 94 | bitClear(data[1], 1); 95 | } 96 | if (PS4.getButtonPress(L1)) { 97 | bitClear(data[1], 2); 98 | } 99 | if (PS4.getButtonPress(R1)) { 100 | bitClear(data[1], 3); 101 | } 102 | if (PS4.getButtonPress(TRIANGLE)) { 103 | bitClear(data[1], 4); 104 | } 105 | if (PS4.getButtonPress(CIRCLE)) { 106 | bitClear(data[1], 5); 107 | } 108 | if (PS4.getButtonPress(CROSS)) { 109 | bitClear(data[1], 6); 110 | } 111 | if (PS4.getButtonPress(SQUARE)) { 112 | bitClear(data[1], 7); 113 | } 114 | 115 | data[2] = PS4.getAnalogHat(RightHatX); 116 | data[3] = PS4.getAnalogHat(RightHatY); 117 | 118 | data[4] = PS4.getAnalogHat(LeftHatX); 119 | data[5] = PS4.getAnalogHat(LeftHatY); 120 | 121 | if (PS4.getButtonClick(PS)) { 122 | PS4.disconnect(); 123 | } 124 | } else { 125 | data[0] = 0xFF; 126 | data[1] = 0xFF; 127 | data[2] = 0x7F; 128 | data[3] = 0x7F; 129 | data[4] = 0x7F; 130 | data[5] = 0x7F; 131 | } 132 | 133 | unsigned long now = millis(); 134 | if (now - lastSync >= 20) { 135 | lastSync = now; 136 | Wire.beginTransmission(I2C_ADDRESS); 137 | Wire.write(data[0]); 138 | Wire.write(data[1]); 139 | Wire.write(data[2]); 140 | Wire.write(data[3]); 141 | Wire.write(data[4]); 142 | Wire.write(data[5]); 143 | Wire.endTransmission(); 144 | } 145 | } 146 | --------------------------------------------------------------------------------